Merge remote-tracking branch 'origin/master' into feature_send_material_profiles

This commit is contained in:
Ian Paschal 2018-07-02 12:37:56 +02:00
commit 8f7370db6c
703 changed files with 272749 additions and 117038 deletions

View file

@ -26,6 +26,6 @@
<screenshots>
<screenshot type="default" width="1280" height="720">http://software.ultimaker.com/Cura.png</screenshot>
</screenshots>
<url type="homepage">https://ultimaker.com/en/products/cura-software?utm_source=cura&utm_medium=software&utm_campaign=resources</url>
<url type="homepage">https://ultimaker.com/en/products/cura-software?utm_source=cura&amp;utm_medium=software&amp;utm_campaign=resources</url>
<translation type="gettext">Cura</translation>
</component>

View file

@ -1,5 +1,7 @@
# 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
@ -17,7 +19,7 @@ class Backups:
## 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) -> (bytes, dict):
def createBackup(self) -> Tuple[Optional[bytes], Optional[dict]]:
return self.manager.createBackup()
## Restore a back-up using the BackupsManager.

View file

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

View file

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

View file

@ -28,12 +28,12 @@ class Backup:
# 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]
## Create a back-up from the current user config folder.
def makeFromCurrent(self) -> (bool, Optional[str]):
def makeFromCurrent(self) -> None:
cura_release = CuraApplication.getInstance().getVersion()
version_data_dir = Resources.getDataStoragePath()
@ -54,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.

View file

@ -1,6 +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, Tuple
from UM.Logger import Logger
from cura.Backups.Backup import Backup
@ -18,7 +18,7 @@ class BackupsManager:
## 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) -> (Optional[bytes], Optional[dict]):
def createBackup(self) -> Tuple[Optional[bytes], Optional[dict]]:
self._disableAutoSave()
backup = Backup()
backup.makeFromCurrent()

View file

@ -1,12 +1,12 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import QObject, QUrl
from PyQt5.QtGui import QDesktopServices
from UM.FlameProfiler import pyqtSlot
from typing import List, TYPE_CHECKING
from UM.Event import CallFunctionEvent
from UM.Application import Application
from UM.FlameProfiler import pyqtSlot
from UM.Math.Vector import Vector
from UM.Scene.Selection import Selection
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
@ -14,6 +14,7 @@ from UM.Operations.GroupedOperation import GroupedOperation
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
from UM.Operations.TranslateOperation import TranslateOperation
import cura.CuraApplication
from cura.Operations.SetParentOperation import SetParentOperation
from cura.MultiplyObjectsJob import MultiplyObjectsJob
from cura.Settings.SetObjectExtruderOperation import SetObjectExtruderOperation
@ -23,28 +24,30 @@ from cura.Operations.SetBuildPlateNumberOperation import SetBuildPlateNumberOper
from UM.Logger import Logger
if TYPE_CHECKING:
from UM.Scene.SceneNode import SceneNode
class CuraActions(QObject):
def __init__(self, parent = None):
def __init__(self, parent: QObject = None) -> None:
super().__init__(parent)
@pyqtSlot()
def openDocumentation(self):
def openDocumentation(self) -> None:
# Starting a web browser from a signal handler connected to a menu will crash on windows.
# So instead, defer the call to the next run of the event loop, since that does work.
# Note that weirdly enough, only signal handlers that open a web browser fail like that.
event = CallFunctionEvent(self._openUrl, [QUrl("http://ultimaker.com/en/support/software")], {})
Application.getInstance().functionEvent(event)
cura.CuraApplication.CuraApplication.getInstance().functionEvent(event)
@pyqtSlot()
def openBugReportPage(self):
def openBugReportPage(self) -> None:
event = CallFunctionEvent(self._openUrl, [QUrl("http://github.com/Ultimaker/Cura/issues")], {})
Application.getInstance().functionEvent(event)
cura.CuraApplication.CuraApplication.getInstance().functionEvent(event)
## Reset camera position and direction to default
@pyqtSlot()
def homeCamera(self) -> None:
scene = Application.getInstance().getController().getScene()
scene = cura.CuraApplication.CuraApplication.getInstance().getController().getScene()
camera = scene.getActiveCamera()
camera.setPosition(Vector(-80, 250, 700))
camera.setPerspective(True)
@ -72,17 +75,17 @@ class CuraActions(QObject):
# \param count The number of times to multiply the selection.
@pyqtSlot(int)
def multiplySelection(self, count: int) -> None:
min_offset = Application.getInstance().getBuildVolume().getEdgeDisallowedSize() + 2 # Allow for some rounding errors
min_offset = cura.CuraApplication.CuraApplication.getInstance().getBuildVolume().getEdgeDisallowedSize() + 2 # Allow for some rounding errors
job = MultiplyObjectsJob(Selection.getAllSelectedObjects(), count, min_offset = max(min_offset, 8))
job.start()
## Delete all selected objects.
@pyqtSlot()
def deleteSelection(self) -> None:
if not Application.getInstance().getController().getToolsEnabled():
if not cura.CuraApplication.CuraApplication.getInstance().getController().getToolsEnabled():
return
removed_group_nodes = []
removed_group_nodes = [] #type: List[SceneNode]
op = GroupedOperation()
nodes = Selection.getAllSelectedObjects()
for node in nodes:
@ -96,7 +99,7 @@ class CuraActions(QObject):
op.addOperation(RemoveSceneNodeOperation(group_node))
# Reset the print information
Application.getInstance().getController().getScene().sceneChanged.emit(node)
cura.CuraApplication.CuraApplication.getInstance().getController().getScene().sceneChanged.emit(node)
op.push()
@ -111,7 +114,7 @@ class CuraActions(QObject):
for node in Selection.getAllSelectedObjects():
# If the node is a group, apply the active extruder to all children of the group.
if node.callDecoration("isGroup"):
for grouped_node in BreadthFirstIterator(node):
for grouped_node in BreadthFirstIterator(node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
if grouped_node.callDecoration("getActiveExtruder") == extruder_id:
continue
@ -143,7 +146,7 @@ class CuraActions(QObject):
Logger.log("d", "Setting build plate number... %d" % build_plate_nr)
operation = GroupedOperation()
root = Application.getInstance().getController().getScene().getRoot()
root = cura.CuraApplication.CuraApplication.getInstance().getController().getScene().getRoot()
nodes_to_change = []
for node in Selection.getAllSelectedObjects():
@ -151,7 +154,7 @@ class CuraActions(QObject):
while parent_node.getParent() != root:
parent_node = parent_node.getParent()
for single_node in BreadthFirstIterator(parent_node):
for single_node in BreadthFirstIterator(parent_node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
nodes_to_change.append(single_node)
if not nodes_to_change:
@ -164,5 +167,5 @@ class CuraActions(QObject):
Selection.clear()
def _openUrl(self, url):
def _openUrl(self, url: QUrl) -> None:
QDesktopServices.openUrl(url)

View file

@ -2,7 +2,6 @@
# Cura is released under the terms of the LGPLv3 or higher.
import copy
import json
import os
import sys
import time
@ -14,7 +13,8 @@ from PyQt5.QtGui import QColor, QIcon
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
from UM.Qt.QtApplication import QtApplication
from typing import cast, TYPE_CHECKING
from UM.Scene.SceneNode import SceneNode
from UM.Scene.Camera import Camera
from UM.Math.Vector import Vector
@ -28,6 +28,8 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Mesh.ReadMeshJob import ReadMeshJob
from UM.Logger import Logger
from UM.Preferences import Preferences
from UM.Qt.QtApplication import QtApplication #The class we're inheriting from.
from UM.View.SelectionPass import SelectionPass #For typing.
from UM.Scene.Selection import Selection
from UM.Scene.GroupDecorator import GroupDecorator
from UM.Settings.ContainerStack import ContainerStack
@ -109,21 +111,19 @@ from UM.FlameProfiler import pyqtSlot
numpy.seterr(all = "ignore")
MYPY = False
if not MYPY:
try:
from cura.CuraVersion import CuraVersion, CuraBuildType, CuraDebugMode
except ImportError:
CuraVersion = "master" # [CodeStyle: Reflecting imported value]
CuraBuildType = ""
CuraDebugMode = False
try:
from cura.CuraVersion import CuraVersion, CuraBuildType, CuraDebugMode
except ImportError:
CuraVersion = "master" # [CodeStyle: Reflecting imported value]
CuraBuildType = ""
CuraDebugMode = False
class CuraApplication(QtApplication):
# SettingVersion represents the set of settings available in the machine/extruder definitions.
# You need to make sure that this version number needs to be increased if there is any non-backwards-compatible
# changes of the settings.
SettingVersion = 4
SettingVersion = 5
Created = False
@ -669,6 +669,7 @@ class CuraApplication(QtApplication):
self._plugins_loaded = True
def run(self):
super().run()
container_registry = self._container_registry
Logger.log("i", "Initializing variant manager")
@ -1719,7 +1720,7 @@ class CuraApplication(QtApplication):
def _onContextMenuRequested(self, x: float, y: float) -> None:
# Ensure we select the object if we request a context menu over an object without having a selection.
if not Selection.hasSelection():
node = self.getController().getScene().findObject(self.getRenderer().getRenderPass("selection").getIdAtPosition(x, y))
node = self.getController().getScene().findObject(cast(SelectionPass, self.getRenderer().getRenderPass("selection")).getIdAtPosition(x, y))
if node:
while(node.getParent() and node.getParent().callDecoration("isGroup")):
node = node.getParent()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,10 +1,10 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Dict, Optional, List
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.
@ -21,11 +21,11 @@ from PyQt5.QtCore import QObject, pyqtSlot
#
class QualityGroup(QObject):
def __init__(self, name: str, quality_type: str, parent = None):
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
@ -33,15 +33,17 @@ class QualityGroup(QObject):
def getName(self) -> str:
return self.name
def getAllKeys(self) -> set:
result = set()
def getAllKeys(self) -> Set[str]:
result = set() #type: Set[str]
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)

View file

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

View file

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

View file

@ -1,6 +1,9 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM.Application import Application
from typing import Optional, TYPE_CHECKING
from UM.Qt.QtApplication import QtApplication
from UM.Math.Vector import Vector
from UM.Resources import Resources
@ -10,19 +13,21 @@ from UM.View.RenderBatch import RenderBatch
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
if TYPE_CHECKING:
from UM.View.GL.ShaderProgram import ShaderProgram
## A RenderPass subclass that renders a the distance of selectable objects from the active camera to a texture.
# The texture is used to map a 2d location (eg the mouse location) to a world space position
#
# 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()
self._renderer = QtApplication.getInstance().getRenderer()
self._shader = None
self._scene = Application.getInstance().getController().getScene()
self._shader = None #type: Optional[ShaderProgram]
self._scene = QtApplication.getInstance().getController().getScene()
def render(self) -> None:
if not self._shader:
@ -37,7 +42,7 @@ class PickingPass(RenderPass):
batch = RenderBatch(self._shader)
# Fill up the batch with objects that can be sliced. `
for node in DepthFirstIterator(self._scene.getRoot()):
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible():
batch.addItem(node.getWorldTransformation(), node.getMeshData())

View file

@ -1,5 +1,8 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional, TYPE_CHECKING
from UM.Application import Application
from UM.Resources import Resources
@ -10,7 +13,8 @@ from UM.View.RenderBatch import RenderBatch
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from typing import Optional
if TYPE_CHECKING:
from UM.View.GL.ShaderProgram import ShaderProgram
MYPY = False
if MYPY:
@ -33,16 +37,16 @@ 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]
self._renderer = Application.getInstance().getRenderer()
self._shader = None
self._non_printing_shader = None
self._support_mesh_shader = None
self._shader = None #type: Optional[ShaderProgram]
self._non_printing_shader = None #type: Optional[ShaderProgram]
self._support_mesh_shader = None #type: Optional[ShaderProgram]
self._scene = Application.getInstance().getController().getScene()
# Set the camera to be used by this render pass
@ -53,20 +57,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)
@ -75,8 +82,8 @@ class PreviewPass(RenderPass):
batch = RenderBatch(self._shader)
batch_support_mesh = RenderBatch(self._support_mesh_shader)
# Fill up the batch with objects that can be sliced. `
for node in DepthFirstIterator(self._scene.getRoot()):
# Fill up the batch with objects that can be sliced.
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible():
per_mesh_stack = node.callDecoration("getStack")
if node.callDecoration("isNonThumbnailVisibleMesh"):

View file

@ -4,10 +4,9 @@
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, pyqtSlot
from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
from typing import Optional
from typing import Optional, TYPE_CHECKING
MYPY = False
if MYPY:
if TYPE_CHECKING:
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel
@ -20,12 +19,12 @@ class ExtruderOutputModel(QObject):
extruderConfigurationChanged = pyqtSignal()
isPreheatingChanged = pyqtSignal()
def __init__(self, printer: "PrinterOutputModel", position, parent=None):
def __init__(self, printer: "PrinterOutputModel", position, parent=None) -> None:
super().__init__(parent)
self._printer = printer
self._position = position
self._target_hotend_temperature = 0
self._hotend_temperature = 0
self._target_hotend_temperature = 0 # type: float
self._hotend_temperature = 0 # type: float
self._hotend_id = ""
self._active_material = None # type: Optional[MaterialOutputModel]
self._extruder_configuration = ExtruderConfigurationModel()
@ -47,7 +46,7 @@ class ExtruderOutputModel(QObject):
return False
@pyqtProperty(QObject, notify = activeMaterialChanged)
def activeMaterial(self) -> "MaterialOutputModel":
def activeMaterial(self) -> Optional["MaterialOutputModel"]:
return self._active_material
def updateActiveMaterial(self, material: Optional["MaterialOutputModel"]):

View file

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

View file

@ -1,17 +1,18 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM.Application import Application
from UM.FileHandler.FileHandler import FileHandler #For typing.
from UM.Logger import Logger
from UM.Scene.SceneNode import SceneNode #For typing.
from cura.CuraApplication import CuraApplication
from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState
from PyQt5.QtNetwork import QHttpMultiPart, QHttpPart, QNetworkRequest, QNetworkAccessManager, QNetworkReply
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, pyqtSignal, QUrl, QCoreApplication
from PyQt5.QtNetwork import QHttpMultiPart, QHttpPart, QNetworkRequest, QNetworkAccessManager, QNetworkReply, QAuthenticator
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl, QCoreApplication
from time import time
from typing import Callable, Any, Optional, Dict, Tuple
from typing import Any, Callable, Dict, List, Optional
from enum import IntEnum
from typing import List
import os # To get the username
import gzip
@ -27,20 +28,20 @@ class AuthState(IntEnum):
class NetworkedPrinterOutputDevice(PrinterOutputDevice):
authenticationStateChanged = pyqtSignal()
def __init__(self, device_id, address: str, properties, parent = None) -> None:
def __init__(self, device_id, address: str, properties: Dict[bytes, bytes], parent: QObject = None) -> None:
super().__init__(device_id = device_id, parent = parent)
self._manager = None # type: QNetworkAccessManager
self._last_manager_create_time = None # type: float
self._manager = None # type: Optional[QNetworkAccessManager]
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
self._properties = properties
self._user_agent = "%s/%s " % (Application.getInstance().getApplicationName(), Application.getInstance().getVersion())
self._user_agent = "%s/%s " % (CuraApplication.getInstance().getApplicationName(), CuraApplication.getInstance().getVersion())
self._onFinishedCallbacks = {} # type: Dict[str, Callable[[QNetworkReply], None]]
self._authentication_state = AuthState.NotAuthenticated
@ -67,16 +68,16 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
self._printer_type = value
break
def requestWrite(self, nodes, file_name=None, filter_by_machine=False, file_handler=None, **kwargs) -> None:
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
raise NotImplementedError("requestWrite needs to be implemented")
def setAuthenticationState(self, authentication_state) -> None:
def setAuthenticationState(self, authentication_state: AuthState) -> None:
if self._authentication_state != authentication_state:
self._authentication_state = authentication_state
self.authenticationStateChanged.emit()
@pyqtProperty(int, notify=authenticationStateChanged)
def authenticationState(self) -> int:
@pyqtProperty(int, notify = authenticationStateChanged)
def authenticationState(self) -> AuthState:
return self._authentication_state
def _compressDataAndNotifyQt(self, data_to_append: str) -> bytes:
@ -121,7 +122,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
self._compressing_gcode = False
return b"".join(file_data_bytes_list)
def _update(self) -> bool:
def _update(self) -> None:
if self._last_response_time:
time_since_last_response = time() - self._last_response_time
else:
@ -144,16 +145,16 @@ 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
return True
def _createEmptyRequest(self, target, content_type: Optional[str] = "application/json") -> QNetworkRequest:
def _createEmptyRequest(self, target: str, content_type: Optional[str] = "application/json") -> QNetworkRequest:
url = QUrl("http://" + self._address + self._api_prefix + target)
request = QNetworkRequest(url)
if content_type is not None:
@ -161,7 +162,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
request.setHeader(QNetworkRequest.UserAgentHeader, self._user_agent)
return request
def createFormPart(self, content_header, data, content_type = None) -> QHttpPart:
def _createFormPart(self, content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart:
part = QHttpPart()
if not content_header.startswith("form-data;"):
@ -187,40 +188,34 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
if reply in self._kept_alive_multiparts:
del self._kept_alive_multiparts[reply]
def put(self, target: str, data: str, onFinished: Optional[Callable[[Any, QNetworkReply], None]]) -> None:
def put(self, target: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None:
if self._manager is None:
self._createNetworkManager()
request = self._createEmptyRequest(target)
self._last_request_time = time()
reply = self._manager.put(request, data.encode())
self._registerOnFinishedCallback(reply, onFinished)
self._registerOnFinishedCallback(reply, on_finished)
def get(self, target: str, onFinished: Optional[Callable[[Any, QNetworkReply], None]]) -> None:
def get(self, target: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None:
if self._manager is None:
self._createNetworkManager()
request = self._createEmptyRequest(target)
self._last_request_time = time()
reply = self._manager.get(request)
self._registerOnFinishedCallback(reply, onFinished)
self._registerOnFinishedCallback(reply, on_finished)
def post(self, target: str, data: str, onFinished: Optional[Callable[[Any, QNetworkReply], None]], onProgress: Callable = None) -> None:
def post(self, target: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> None:
if self._manager is None:
self._createNetworkManager()
request = self._createEmptyRequest(target)
self._last_request_time = time()
reply = self._manager.post(request, data)
if onProgress is not None:
reply.uploadProgress.connect(onProgress)
self._registerOnFinishedCallback(reply, onFinished)
if on_progress is not None:
reply.uploadProgress.connect(on_progress)
self._registerOnFinishedCallback(reply, on_finished)
def postFormWithParts(self, target: str, parts: List[QHttpPart], on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> QNetworkReply:
## Send an API call to the printer via HTTP POST.
# \param target The target URL on the printer.
# \param parts The parts of a form. One must be provided for each form
# element in the POST call. Create form parts using _createFormPart.
# \param onFinished A function to call when the call has been completed.
# \param onProgress A function to call when progress has been made. Use
# this to update a progress bar.
def postFormWithParts(self, target: str, parts: List[QHttpPart], onFinished: Optional[Callable[[Any, QNetworkReply], None]] = None, onProgress: Callable = None) -> None:
if self._manager is None:
self._createNetworkManager()
request = self._createEmptyRequest(target, content_type=None)
@ -234,20 +229,20 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
self._kept_alive_multiparts[reply] = multi_post_part
if onProgress is not None:
reply.uploadProgress.connect(onProgress)
self._registerOnFinishedCallback(reply, onFinished)
if on_progress is not None:
reply.uploadProgress.connect(on_progress)
self._registerOnFinishedCallback(reply, on_finished)
return reply
def postForm(self, target: str, header_data: str, body_data: bytes, onFinished: Optional[Callable[[Any, QNetworkReply], None]], onProgress: Callable = None) -> None:
def postForm(self, target: str, header_data: str, body_data: bytes, on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> None:
post_part = QHttpPart()
post_part.setHeader(QNetworkRequest.ContentDispositionHeader, header_data)
post_part.setBody(body_data)
self.postFormWithParts(target, [post_part], onFinished, onProgress)
self.postFormWithParts(target, [post_part], on_finished, on_progress)
def _onAuthenticationRequired(self, reply, authenticator) -> None:
def _onAuthenticationRequired(self, reply: QNetworkReply, authenticator: QAuthenticator) -> None:
Logger.log("w", "Request to {url} required authentication, which was not implemented".format(url = reply.url().toString()))
def _createNetworkManager(self) -> None:
@ -262,11 +257,11 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
self._manager.authenticationRequired.connect(self._onAuthenticationRequired)
if self._properties.get(b"temporary", b"false") != b"true":
Application.getInstance().getMachineManager().checkCorrectGroupName(self.getId(), self.name)
CuraApplication.getInstance().getMachineManager().checkCorrectGroupName(self.getId(), self.name)
def _registerOnFinishedCallback(self, reply: QNetworkReply, onFinished: Optional[Callable[[Any, QNetworkReply], None]]) -> None:
if onFinished is not None:
self._onFinishedCallbacks[reply.url().toString() + str(reply.operation())] = onFinished
def _registerOnFinishedCallback(self, reply: QNetworkReply, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None:
if on_finished is not None:
self._onFinishedCallbacks[reply.url().toString() + str(reply.operation())] = on_finished
def __handleOnFinished(self, reply: QNetworkReply) -> None:
# Due to garbage collection, we need to cache certain bits of post operations.
@ -303,30 +298,30 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
## Get the unique key of this machine
# \return key String containing the key of the machine.
@pyqtProperty(str, constant=True)
@pyqtProperty(str, constant = True)
def key(self) -> str:
return self._id
## The IP address of the printer.
@pyqtProperty(str, constant=True)
@pyqtProperty(str, constant = True)
def address(self) -> str:
return self._properties.get(b"address", b"").decode("utf-8")
## Name of the printer (as returned from the ZeroConf properties)
@pyqtProperty(str, constant=True)
@pyqtProperty(str, constant = True)
def name(self) -> str:
return self._properties.get(b"name", b"").decode("utf-8")
## Firmware version (as returned from the ZeroConf properties)
@pyqtProperty(str, constant=True)
@pyqtProperty(str, constant = True)
def firmwareVersion(self) -> str:
return self._properties.get(b"firmware_version", b"").decode("utf-8")
@pyqtProperty(str, constant=True)
@pyqtProperty(str, constant = True)
def printerType(self) -> str:
return self._printer_type
## IPadress of this printer
@pyqtProperty(str, constant=True)
## IP adress of this printer
@pyqtProperty(str, constant = True)
def ipAddress(self) -> str:
return self._address

View file

@ -2,9 +2,9 @@
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, pyqtSlot
from typing import Optional
MYPY = False
if MYPY:
from typing import Optional, TYPE_CHECKING
if TYPE_CHECKING:
from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
@ -18,7 +18,7 @@ class PrintJobOutputModel(QObject):
assignedPrinterChanged = pyqtSignal()
ownerChanged = pyqtSignal()
def __init__(self, output_controller: "PrinterOutputController", key: str = "", name: str = "", parent=None):
def __init__(self, output_controller: "PrinterOutputController", key: str = "", name: str = "", parent=None) -> None:
super().__init__(parent)
self._output_controller = output_controller
self._state = ""

View file

@ -27,7 +27,7 @@ class PrinterOutputModel(QObject):
cameraChanged = pyqtSignal()
configurationChanged = pyqtSignal()
def __init__(self, output_controller: "PrinterOutputController", number_of_extruders: int = 1, parent=None, firmware_version = ""):
def __init__(self, output_controller: "PrinterOutputController", number_of_extruders: int = 1, parent=None, firmware_version = "") -> None:
super().__init__(parent)
self._bed_temperature = -1 # Use -1 for no heated bed.
self._target_bed_temperature = 0

View file

@ -1,17 +1,20 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM.Decorators import deprecated
from UM.i18n import i18nCatalog
from UM.OutputDevice.OutputDevice import OutputDevice
from PyQt5.QtCore import pyqtProperty, QObject, QTimer, pyqtSignal, QVariant
from PyQt5.QtCore import pyqtProperty, QObject, QTimer, pyqtSignal
from PyQt5.QtWidgets import QMessageBox
from UM.Logger import Logger
from UM.FileHandler.FileHandler import FileHandler #For typing.
from UM.Scene.SceneNode import SceneNode #For typing.
from UM.Signal import signalemitter
from UM.Application import Application
from UM.Qt.QtApplication import QtApplication
from enum import IntEnum # For the connection state tracking.
from typing import List, Optional
from typing import Callable, List, Optional
MYPY = False
if MYPY:
@ -20,6 +23,16 @@ if MYPY:
i18n_catalog = i18nCatalog("cura")
## The current processing state of the backend.
class ConnectionState(IntEnum):
closed = 0
connecting = 1
connected = 2
busy = 3
error = 4
## Printer output device adds extra interface options on top of output device.
#
# The assumption is made the printer is a FDM printer.
@ -47,38 +60,37 @@ class PrinterOutputDevice(QObject, OutputDevice):
# Signal to indicate that the configuration of one of the printers has changed.
uniqueConfigurationsChanged = pyqtSignal()
def __init__(self, device_id, parent = None):
def __init__(self, device_id: str, parent: QObject = None) -> None:
super().__init__(device_id = device_id, parent = parent)
self._printers = [] # type: List[PrinterOutputModel]
self._unique_configurations = [] # type: List[ConfigurationModel]
self._monitor_view_qml_path = ""
self._monitor_component = None
self._monitor_item = None
self._monitor_view_qml_path = "" #type: str
self._monitor_component = None #type: Optional[QObject]
self._monitor_item = None #type: Optional[QObject]
self._control_view_qml_path = ""
self._control_component = None
self._control_item = None
self._control_view_qml_path = "" #type: str
self._control_component = None #type: Optional[QObject]
self._control_item = None #type: Optional[QObject]
self._qml_context = None
self._accepts_commands = False
self._accepts_commands = False #type: bool
self._update_timer = QTimer()
self._update_timer = QTimer() #type: QTimer
self._update_timer.setInterval(2000) # TODO; Add preference for update interval
self._update_timer.setSingleShot(False)
self._update_timer.timeout.connect(self._update)
self._connection_state = ConnectionState.closed
self._connection_state = ConnectionState.closed #type: ConnectionState
self._firmware_name = None
self._address = ""
self._connection_text = ""
self._firmware_name = None #type: Optional[str]
self._address = "" #type: str
self._connection_text = "" #type: str
self.printersChanged.connect(self._onPrintersChanged)
Application.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._updateUniqueConfigurations)
QtApplication.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._updateUniqueConfigurations)
@pyqtProperty(str, notify = connectionTextChanged)
def address(self):
def address(self) -> str:
return self._address
def setConnectionText(self, connection_text):
@ -87,36 +99,36 @@ class PrinterOutputDevice(QObject, OutputDevice):
self.connectionTextChanged.emit()
@pyqtProperty(str, constant=True)
def connectionText(self):
def connectionText(self) -> str:
return self._connection_text
def materialHotendChangedMessage(self, callback):
def materialHotendChangedMessage(self, callback: Callable[[int], None]) -> None:
Logger.log("w", "materialHotendChangedMessage needs to be implemented, returning 'Yes'")
callback(QMessageBox.Yes)
def isConnected(self):
def isConnected(self) -> bool:
return self._connection_state != ConnectionState.closed and self._connection_state != ConnectionState.error
def setConnectionState(self, connection_state):
def setConnectionState(self, connection_state: ConnectionState) -> None:
if self._connection_state != connection_state:
self._connection_state = connection_state
self.connectionStateChanged.emit(self._id)
@pyqtProperty(str, notify = connectionStateChanged)
def connectionState(self):
def connectionState(self) -> ConnectionState:
return self._connection_state
def _update(self):
def _update(self) -> None:
pass
def _getPrinterByKey(self, key) -> Optional["PrinterOutputModel"]:
def _getPrinterByKey(self, key: str) -> Optional["PrinterOutputModel"]:
for printer in self._printers:
if printer.key == key:
return printer
return None
def requestWrite(self, nodes, file_name = None, filter_by_machine = False, file_handler = None, **kwargs):
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
raise NotImplementedError("requestWrite needs to be implemented")
@pyqtProperty(QObject, notify = printersChanged)
@ -126,11 +138,11 @@ class PrinterOutputDevice(QObject, OutputDevice):
return None
@pyqtProperty("QVariantList", notify = printersChanged)
def printers(self):
def printers(self) -> List["PrinterOutputModel"]:
return self._printers
@pyqtProperty(QObject, constant=True)
def monitorItem(self):
@pyqtProperty(QObject, constant = True)
def monitorItem(self) -> QObject:
# Note that we specifically only check if the monitor component is created.
# It could be that it failed to actually create the qml item! If we check if the item was created, it will try to
# create the item (and fail) every time.
@ -138,49 +150,49 @@ class PrinterOutputDevice(QObject, OutputDevice):
self._createMonitorViewFromQML()
return self._monitor_item
@pyqtProperty(QObject, constant=True)
def controlItem(self):
@pyqtProperty(QObject, constant = True)
def controlItem(self) -> QObject:
if not self._control_component:
self._createControlViewFromQML()
return self._control_item
def _createControlViewFromQML(self):
def _createControlViewFromQML(self) -> None:
if not self._control_view_qml_path:
return
if self._control_item is None:
self._control_item = Application.getInstance().createQmlComponent(self._control_view_qml_path, {"OutputDevice": self})
self._control_item = QtApplication.getInstance().createQmlComponent(self._control_view_qml_path, {"OutputDevice": self})
def _createMonitorViewFromQML(self):
def _createMonitorViewFromQML(self) -> None:
if not self._monitor_view_qml_path:
return
if self._monitor_item is None:
self._monitor_item = Application.getInstance().createQmlComponent(self._monitor_view_qml_path, {"OutputDevice": self})
self._monitor_item = QtApplication.getInstance().createQmlComponent(self._monitor_view_qml_path, {"OutputDevice": self})
## Attempt to establish connection
def connect(self):
def connect(self) -> None:
self.setConnectionState(ConnectionState.connecting)
self._update_timer.start()
## Attempt to close the connection
def close(self):
def close(self) -> None:
self._update_timer.stop()
self.setConnectionState(ConnectionState.closed)
## Ensure that close gets called when object is destroyed
def __del__(self):
def __del__(self) -> None:
self.close()
@pyqtProperty(bool, notify=acceptsCommandsChanged)
def acceptsCommands(self):
@pyqtProperty(bool, notify = acceptsCommandsChanged)
def acceptsCommands(self) -> bool:
return self._accepts_commands
@deprecated("Please use the protected function instead", "3.2")
def setAcceptsCommands(self, accepts_commands):
def setAcceptsCommands(self, accepts_commands: bool) -> None:
self._setAcceptsCommands(accepts_commands)
## Set a flag to signal the UI that the printer is not (yet) ready to receive commands
def _setAcceptsCommands(self, accepts_commands):
def _setAcceptsCommands(self, accepts_commands: bool) -> None:
if self._accepts_commands != accepts_commands:
self._accepts_commands = accepts_commands
@ -188,15 +200,15 @@ class PrinterOutputDevice(QObject, OutputDevice):
# Returns the unique configurations of the printers within this output device
@pyqtProperty("QVariantList", notify = uniqueConfigurationsChanged)
def uniqueConfigurations(self):
def uniqueConfigurations(self) -> List["ConfigurationModel"]:
return self._unique_configurations
def _updateUniqueConfigurations(self):
def _updateUniqueConfigurations(self) -> None:
self._unique_configurations = list(set([printer.printerConfiguration for printer in self._printers if printer.printerConfiguration is not None]))
self._unique_configurations.sort(key = lambda k: k.printerType)
self.uniqueConfigurationsChanged.emit()
def _onPrintersChanged(self):
def _onPrintersChanged(self) -> None:
for printer in self._printers:
printer.configurationChanged.connect(self._updateUniqueConfigurations)
@ -205,21 +217,12 @@ class PrinterOutputDevice(QObject, OutputDevice):
## Set the device firmware name
#
# \param name \type{str} The name of the firmware.
def _setFirmwareName(self, name):
# \param name The name of the firmware.
def _setFirmwareName(self, name: str) -> None:
self._firmware_name = name
## Get the name of device firmware
#
# This name can be used to define device type
def getFirmwareName(self):
def getFirmwareName(self) -> Optional[str]:
return self._firmware_name
## The current processing state of the backend.
class ConnectionState(IntEnum):
closed = 0
connecting = 1
connected = 2
busy = 3
error = 4

View file

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

View file

@ -1,40 +1,47 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from copy import deepcopy
from typing import List, Optional
from typing import cast, Dict, List, Optional
from UM.Application import Application
from UM.Math.AxisAlignedBox import AxisAlignedBox
from UM.Math.Polygon import Polygon #For typing.
from UM.Scene.SceneNode import SceneNode
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator #To cast the deepcopy of every decorator back to SceneNodeDecorator.
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
import cura.CuraApplication #To get the build plate.
from cura.Settings.ExtruderStack import ExtruderStack #For typing.
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator #For per-object settings.
## Scene nodes that are models are only seen when selecting the corresponding build plate
# Note that many other nodes can just be UM SceneNode objects.
class CuraSceneNode(SceneNode):
def __init__(self, parent: Optional["SceneNode"] = None, visible: bool = True, name: str = "", no_setting_override: bool = False):
def __init__(self, parent: Optional["SceneNode"] = None, visible: bool = True, name: str = "", no_setting_override: bool = False) -> None:
super().__init__(parent = parent, visible = visible, name = name)
if not no_setting_override:
self.addDecorator(SettingOverrideDecorator()) # now we always have a getActiveExtruderPosition, unless explicitly disabled
self._outside_buildarea = False
def setOutsideBuildArea(self, new_value):
def setOutsideBuildArea(self, new_value: bool) -> None:
self._outside_buildarea = new_value
def isOutsideBuildArea(self):
def isOutsideBuildArea(self) -> bool:
return self._outside_buildarea or self.callDecoration("getBuildPlateNumber") < 0
def isVisible(self):
return super().isVisible() and self.callDecoration("getBuildPlateNumber") == Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
def isVisible(self) -> bool:
return super().isVisible() and self.callDecoration("getBuildPlateNumber") == cura.CuraApplication.CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
def isSelectable(self) -> bool:
return super().isSelectable() and self.callDecoration("getBuildPlateNumber") == Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
return super().isSelectable() and self.callDecoration("getBuildPlateNumber") == cura.CuraApplication.CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
## Get the extruder used to print this node. If there is no active node, then the extruder in position zero is returned
# TODO The best way to do it is by adding the setActiveExtruder decorator to every node when is loaded
def getPrintingExtruder(self):
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())
@ -79,17 +86,17 @@ class CuraSceneNode(SceneNode):
]
## Return if the provided bbox collides with the bbox of this scene node
def collidesWithBbox(self, check_bbox):
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
## Return if any area collides with the convex hull of this scene node
def collidesWithArea(self, areas):
def collidesWithArea(self, areas: List[Polygon]) -> bool:
convex_hull = self.callDecoration("getConvexHull")
if convex_hull:
if not convex_hull.isValid():
@ -104,8 +111,7 @@ class CuraSceneNode(SceneNode):
return False
## Override of SceneNode._calculateAABB to exclude non-printing-meshes from bounding box
def _calculateAABB(self):
aabb = None
def _calculateAABB(self) -> None:
if self._mesh_data:
aabb = self._mesh_data.getExtents(self.getWorldTransformation())
else: # If there is no mesh_data, use a boundingbox that encompasses the local (0,0,0)
@ -123,18 +129,18 @@ class CuraSceneNode(SceneNode):
self._aabb = aabb
## Taken from SceneNode, but replaced SceneNode with CuraSceneNode
def __deepcopy__(self, memo):
def __deepcopy__(self, memo: Dict[int, object]) -> "CuraSceneNode":
copy = CuraSceneNode(no_setting_override = True) # Setting override will be added later
copy.setTransformation(self.getLocalTransformation())
copy.setMeshData(self._mesh_data)
copy.setVisible(deepcopy(self._visible, memo))
copy._selectable = deepcopy(self._selectable, memo)
copy._name = deepcopy(self._name, memo)
copy.setVisible(cast(bool, deepcopy(self._visible, memo)))
copy._selectable = cast(bool, deepcopy(self._selectable, memo))
copy._name = cast(str, deepcopy(self._name, memo))
for decorator in self._decorators:
copy.addDecorator(deepcopy(decorator, memo))
copy.addDecorator(cast(SceneNodeDecorator, deepcopy(decorator, memo)))
for child in self._children:
copy.addChild(deepcopy(child, memo))
copy.addChild(cast(SceneNode, deepcopy(child, memo)))
self.calculateBoundingBoxMesh()
return copy

View file

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

View file

@ -5,7 +5,7 @@ import os
import re
import configparser
from typing import Optional
from typing import cast, Optional
from PyQt5.QtWidgets import QMessageBox
@ -26,7 +26,7 @@ from UM.Resources import Resources
from . import ExtruderStack
from . import GlobalStack
from cura.CuraApplication import CuraApplication
import cura.CuraApplication
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
from cura.ReaderWriters.ProfileReader import NoProfileException
@ -57,7 +57,7 @@ class CuraContainerRegistry(ContainerRegistry):
if isinstance(container, InstanceContainer) and type(container) != type(self.getEmptyInstanceContainer()):
# Check against setting version of the definition.
required_setting_version = CuraApplication.SettingVersion
required_setting_version = cura.CuraApplication.CuraApplication.SettingVersion
actual_setting_version = int(container.getMetaDataEntry("setting_version", default = 0))
if required_setting_version != actual_setting_version:
Logger.log("w", "Instance container {container_id} is outdated. Its setting version is {actual_setting_version} but it should be {required_setting_version}.".format(container_id = container.getId(), actual_setting_version = actual_setting_version, required_setting_version = required_setting_version))
@ -260,7 +260,7 @@ class CuraContainerRegistry(ContainerRegistry):
profile_id = ContainerRegistry.getInstance().uniqueName(global_stack.getId() + "_extruder_" + str(idx + 1))
profile = InstanceContainer(profile_id)
profile.setName(quality_name)
profile.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
profile.addMetaDataEntry("setting_version", cura.CuraApplication.CuraApplication.SettingVersion)
profile.addMetaDataEntry("type", "quality_changes")
profile.addMetaDataEntry("definition", expected_machine_definition)
profile.addMetaDataEntry("quality_type", quality_type)
@ -356,13 +356,15 @@ 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)
# Check to make sure the imported profile actually makes sense in context of the current configuration.
# This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as
# successfully imported but then fail to show up.
quality_manager = CuraApplication.getInstance()._quality_manager
quality_manager = cura.CuraApplication.CuraApplication.getInstance()._quality_manager
quality_group_dict = quality_manager.getQualityGroupsForMachineDefinition(global_stack)
if quality_type not in quality_group_dict:
return catalog.i18nc("@info:status", "Could not find a quality type {0} for the current configuration.", quality_type)
@ -465,7 +467,7 @@ class CuraContainerRegistry(ContainerRegistry):
def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id, new_global_quality_changes = None, create_new_ids = True):
new_extruder_id = extruder_id
application = CuraApplication.getInstance()
application = cura.CuraApplication.CuraApplication.getInstance()
extruder_definitions = self.findDefinitionContainers(id = new_extruder_id)
if not extruder_definitions:
@ -485,7 +487,7 @@ class CuraContainerRegistry(ContainerRegistry):
definition_changes_name = definition_changes_id
definition_changes = InstanceContainer(definition_changes_id, parent = application)
definition_changes.setName(definition_changes_name)
definition_changes.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
definition_changes.addMetaDataEntry("setting_version", application.SettingVersion)
definition_changes.addMetaDataEntry("type", "definition_changes")
definition_changes.addMetaDataEntry("definition", extruder_definition.getId())
@ -514,7 +516,7 @@ class CuraContainerRegistry(ContainerRegistry):
user_container.setName(user_container_name)
user_container.addMetaDataEntry("type", "user")
user_container.addMetaDataEntry("machine", machine.getId())
user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
user_container.addMetaDataEntry("setting_version", application.SettingVersion)
user_container.setDefinition(machine.definition.getId())
user_container.setMetaDataEntry("position", extruder_stack.getMetaDataEntry("position"))
@ -587,7 +589,7 @@ class CuraContainerRegistry(ContainerRegistry):
extruder_quality_changes_container = InstanceContainer(container_id, parent = application)
extruder_quality_changes_container.setName(container_name)
extruder_quality_changes_container.addMetaDataEntry("type", "quality_changes")
extruder_quality_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
extruder_quality_changes_container.addMetaDataEntry("setting_version", application.SettingVersion)
extruder_quality_changes_container.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
extruder_quality_changes_container.addMetaDataEntry("quality_type", machine_quality_changes.getMetaDataEntry("quality_type"))
extruder_quality_changes_container.setDefinition(machine_quality_changes.getDefinition().getId())
@ -675,7 +677,7 @@ class CuraContainerRegistry(ContainerRegistry):
return extruder_stack
def _findQualityChangesContainerInCuraFolder(self, name):
quality_changes_dir = Resources.getPath(CuraApplication.ResourceTypes.QualityChangesInstanceContainer)
quality_changes_dir = Resources.getPath(cura.CuraApplication.CuraApplication.ResourceTypes.QualityChangesInstanceContainer)
instance_container = None
@ -731,3 +733,9 @@ class CuraContainerRegistry(ContainerRegistry):
extruder_stack.setNextStack(machines[0])
else:
Logger.log("w", "Could not find machine {machine} for extruder {extruder}", machine = extruder_stack.getMetaDataEntry("machine"), extruder = extruder_stack.getId())
#Override just for the type.
@classmethod
@override(ContainerRegistry)
def getInstance(cls, *args, **kwargs) -> "CuraContainerRegistry":
return cast(CuraContainerRegistry, super().getInstance(*args, **kwargs))

View file

@ -1,15 +1,12 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os.path
from typing import Any, Optional
from typing import Any, cast, List, Optional, Union
from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject
from UM.FlameProfiler import pyqtSlot
from UM.Application import Application
from UM.Decorators import override
from UM.FlameProfiler import pyqtSlot
from UM.Logger import Logger
from UM.Settings.ContainerStack import ContainerStack, InvalidContainerStackError
from UM.Settings.InstanceContainer import InstanceContainer
@ -39,19 +36,19 @@ from . import Exceptions
# This also means that operations on the stack that modifies the container ordering is prohibited and
# will raise an exception.
class CuraContainerStack(ContainerStack):
def __init__(self, container_id: str):
def __init__(self, container_id: str) -> None:
super().__init__(container_id)
self._container_registry = ContainerRegistry.getInstance()
self._container_registry = ContainerRegistry.getInstance() #type: ContainerRegistry
self._empty_instance_container = self._container_registry.getEmptyInstanceContainer()
self._empty_instance_container = self._container_registry.getEmptyInstanceContainer() #type: InstanceContainer
self._empty_quality_changes = self._container_registry.findInstanceContainers(id = "empty_quality_changes")[0]
self._empty_quality = self._container_registry.findInstanceContainers(id = "empty_quality")[0]
self._empty_material = self._container_registry.findInstanceContainers(id = "empty_material")[0]
self._empty_variant = self._container_registry.findInstanceContainers(id = "empty_variant")[0]
self._empty_quality_changes = self._container_registry.findInstanceContainers(id = "empty_quality_changes")[0] #type: InstanceContainer
self._empty_quality = self._container_registry.findInstanceContainers(id = "empty_quality")[0] #type: InstanceContainer
self._empty_material = self._container_registry.findInstanceContainers(id = "empty_material")[0] #type: InstanceContainer
self._empty_variant = self._container_registry.findInstanceContainers(id = "empty_variant")[0] #type: InstanceContainer
self._containers = [self._empty_instance_container for i in range(len(_ContainerIndexes.IndexTypeMap))]
self._containers = [self._empty_instance_container for i in range(len(_ContainerIndexes.IndexTypeMap))] #type: List[Union[InstanceContainer, DefinitionContainer]]
self._containers[_ContainerIndexes.QualityChanges] = self._empty_quality_changes
self._containers[_ContainerIndexes.Quality] = self._empty_quality
self._containers[_ContainerIndexes.Material] = self._empty_material
@ -76,7 +73,7 @@ class CuraContainerStack(ContainerStack):
# \return The user changes container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setUserChanges, notify = pyqtContainersChanged)
def userChanges(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.UserChanges]
return cast(InstanceContainer, self._containers[_ContainerIndexes.UserChanges])
## Set the quality changes container.
#
@ -89,12 +86,12 @@ class CuraContainerStack(ContainerStack):
# \return The quality changes container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setQualityChanges, notify = pyqtContainersChanged)
def qualityChanges(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.QualityChanges]
return cast(InstanceContainer, self._containers[_ContainerIndexes.QualityChanges])
## Set the quality container.
#
# \param new_quality The new quality container. It is expected to have a "type" metadata entry with the value "quality".
def setQuality(self, new_quality: InstanceContainer, postpone_emit = False) -> None:
def setQuality(self, new_quality: InstanceContainer, postpone_emit: bool = False) -> None:
self.replaceContainer(_ContainerIndexes.Quality, new_quality, postpone_emit = postpone_emit)
## Get the quality container.
@ -102,12 +99,12 @@ class CuraContainerStack(ContainerStack):
# \return The quality container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setQuality, notify = pyqtContainersChanged)
def quality(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.Quality]
return cast(InstanceContainer, self._containers[_ContainerIndexes.Quality])
## Set the material container.
#
# \param new_material The new material container. It is expected to have a "type" metadata entry with the value "material".
def setMaterial(self, new_material: InstanceContainer, postpone_emit = False) -> None:
def setMaterial(self, new_material: InstanceContainer, postpone_emit: bool = False) -> None:
self.replaceContainer(_ContainerIndexes.Material, new_material, postpone_emit = postpone_emit)
## Get the material container.
@ -115,7 +112,7 @@ class CuraContainerStack(ContainerStack):
# \return The material container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setMaterial, notify = pyqtContainersChanged)
def material(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.Material]
return cast(InstanceContainer, self._containers[_ContainerIndexes.Material])
## Set the variant container.
#
@ -128,7 +125,7 @@ class CuraContainerStack(ContainerStack):
# \return The variant container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setVariant, notify = pyqtContainersChanged)
def variant(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.Variant]
return cast(InstanceContainer, self._containers[_ContainerIndexes.Variant])
## Set the definition changes container.
#
@ -141,7 +138,7 @@ class CuraContainerStack(ContainerStack):
# \return The definition changes container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setDefinitionChanges, notify = pyqtContainersChanged)
def definitionChanges(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.DefinitionChanges]
return cast(InstanceContainer, self._containers[_ContainerIndexes.DefinitionChanges])
## Set the definition container.
#
@ -154,7 +151,7 @@ class CuraContainerStack(ContainerStack):
# \return The definition container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(QObject, fset = setDefinition, notify = pyqtContainersChanged)
def definition(self) -> DefinitionContainer:
return self._containers[_ContainerIndexes.Definition]
return cast(DefinitionContainer, self._containers[_ContainerIndexes.Definition])
@override(ContainerStack)
def getBottom(self) -> "DefinitionContainer":
@ -189,13 +186,9 @@ class CuraContainerStack(ContainerStack):
# \param key The key of the setting to set.
# \param property_name The name of the property to set.
# \param new_value The new value to set the property to.
# \param target_container The type of the container to set the property of. Defaults to "user".
def setProperty(self, key: str, property_name: str, new_value: Any, target_container: str = "user") -> None:
container_index = _ContainerIndexes.TypeIndexMap.get(target_container, -1)
if container_index != -1:
self._containers[container_index].setProperty(key, property_name, new_value)
else:
raise IndexError("Invalid target container {type}".format(type = target_container))
def setProperty(self, key: str, property_name: str, property_value: Any, container: "ContainerInterface" = None, set_from_cache: bool = False) -> None:
container_index = _ContainerIndexes.UserChanges
self._containers[container_index].setProperty(key, property_name, property_value, container, set_from_cache)
## Overridden from ContainerStack
#
@ -310,15 +303,15 @@ class CuraContainerStack(ContainerStack):
#
# \return The ID of the definition container to use when searching for instance containers.
@classmethod
def _findInstanceContainerDefinitionId(cls, machine_definition: DefinitionContainer) -> str:
def _findInstanceContainerDefinitionId(cls, machine_definition: DefinitionContainerInterface) -> str:
quality_definition = machine_definition.getMetaDataEntry("quality_definition")
if not quality_definition:
return machine_definition.id
return machine_definition.id #type: ignore
definitions = ContainerRegistry.getInstance().findDefinitionContainers(id = quality_definition)
if not definitions:
Logger.log("w", "Unable to find parent definition {parent} for machine {machine}", parent = quality_definition, machine = machine_definition.id)
return machine_definition.id
Logger.log("w", "Unable to find parent definition {parent} for machine {machine}", parent = quality_definition, machine = machine_definition.id) #type: ignore
return machine_definition.id #type: ignore
return cls._findInstanceContainerDefinitionId(definitions[0])

View file

@ -1,10 +1,10 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant # For communicating data and events to Qt.
from UM.FlameProfiler import pyqtSlot
from UM.Application import Application # To get the global container stack to find the current machine.
import cura.CuraApplication #To get the global container stack to find the current machine.
from UM.Logger import Logger
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.SceneNode import SceneNode
@ -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
@ -36,14 +36,13 @@ class ExtruderManager(QObject):
super().__init__(parent)
self._application = Application.getInstance()
self._application = cura.CuraApplication.CuraApplication.getInstance()
self._extruder_trains = {} # Per machine, a dictionary of extruder container stack IDs. Only for separately defined extruders.
self._active_extruder_index = -1 # Indicates the index of the active extruder stack. -1 means no active extruder stack
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.
#
@ -132,7 +136,7 @@ class ExtruderManager(QObject):
selected_nodes = []
for node in Selection.getAllSelectedObjects():
if node.callDecoration("isGroup"):
for grouped_node in BreadthFirstIterator(node):
for grouped_node in BreadthFirstIterator(node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
if grouped_node.callDecoration("isGroup"):
continue
@ -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,14 +258,14 @@ 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:
return []
# Get the extruders of all printable meshes in the scene
meshes = [node for node in DepthFirstIterator(scene_root) if isinstance(node, SceneNode) and node.isSelectable()]
meshes = [node for node in DepthFirstIterator(scene_root) if isinstance(node, SceneNode) and node.isSelectable()] #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
for mesh in meshes:
extruder_stack_id = mesh.callDecoration("getActiveExtruder")
if not extruder_stack_id:
@ -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 []
@ -471,7 +477,7 @@ class ExtruderManager(QObject):
# If no extruder has the value, the list will contain the global value.
@staticmethod
def getExtruderValues(key):
global_stack = Application.getInstance().getGlobalContainerStack()
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
result = []
for extruder in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()):
@ -506,7 +512,7 @@ class ExtruderManager(QObject):
# If no extruder has the value, the list will contain the global value.
@staticmethod
def getDefaultExtruderValues(key):
global_stack = Application.getInstance().getGlobalContainerStack()
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
context = PropertyEvaluationContext(global_stack)
context.context["evaluate_from_container_index"] = 1 # skip the user settings container
context.context["override_operators"] = {
@ -539,7 +545,7 @@ class ExtruderManager(QObject):
## Return the default extruder position from the machine manager
@staticmethod
def getDefaultExtruderPosition() -> str:
return Application.getInstance().getMachineManager().defaultExtruderPosition
return cura.CuraApplication.CuraApplication.getInstance().getMachineManager().defaultExtruderPosition
## Get all extruder values for a certain setting.
#
@ -564,7 +570,7 @@ class ExtruderManager(QObject):
@staticmethod
def getExtruderValue(extruder_index, key):
if extruder_index == -1:
extruder_index = int(Application.getInstance().getMachineManager().defaultExtruderPosition)
extruder_index = int(cura.CuraApplication.CuraApplication.getInstance().getMachineManager().defaultExtruderPosition)
extruder = ExtruderManager.getInstance().getExtruderStack(extruder_index)
if extruder:
@ -573,7 +579,7 @@ class ExtruderManager(QObject):
value = value(extruder)
else:
# Just a value from global.
value = Application.getInstance().getGlobalContainerStack().getProperty(key, "value")
value = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack().getProperty(key, "value")
return value
@ -602,7 +608,7 @@ class ExtruderManager(QObject):
if isinstance(value, SettingFunction):
value = value(extruder, context = context)
else: # Just a value from global.
value = Application.getInstance().getGlobalContainerStack().getProperty(key, "value", context = context)
value = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack().getProperty(key, "value", context = context)
return value
@ -615,7 +621,7 @@ class ExtruderManager(QObject):
# \return The effective value
@staticmethod
def getResolveOrValue(key):
global_stack = Application.getInstance().getGlobalContainerStack()
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
resolved_value = global_stack.getProperty(key, "value")
return resolved_value
@ -629,7 +635,7 @@ class ExtruderManager(QObject):
# \return The effective value
@staticmethod
def getDefaultResolveOrValue(key):
global_stack = Application.getInstance().getGlobalContainerStack()
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
context = PropertyEvaluationContext(global_stack)
context.context["evaluate_from_container_index"] = 1 # skip the user settings container
context.context["override_operators"] = {
@ -642,7 +648,7 @@ class ExtruderManager(QObject):
return resolved_value
__instance = None
__instance = None # type: ExtruderManager
@classmethod
def getInstance(cls, *args, **kwargs) -> "ExtruderManager":

View file

@ -1,11 +1,10 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Any, TYPE_CHECKING, Optional
from typing import Any, Dict, TYPE_CHECKING, Optional
from PyQt5.QtCore import pyqtProperty, pyqtSignal
from UM.Application import Application
from UM.Decorators import override
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
from UM.Settings.ContainerStack import ContainerStack
@ -13,6 +12,8 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.Interfaces import ContainerInterface, PropertyEvaluationContext
from UM.Util import parseBool
import cura.CuraApplication
from . import Exceptions
from .CuraContainerStack import CuraContainerStack, _ContainerIndexes
from .ExtruderManager import ExtruderManager
@ -25,7 +26,7 @@ if TYPE_CHECKING:
#
#
class ExtruderStack(CuraContainerStack):
def __init__(self, container_id: str):
def __init__(self, container_id: str) -> None:
super().__init__(container_id)
self.addMetaDataEntry("type", "extruder_train") # For backward compatibility
@ -50,14 +51,14 @@ class ExtruderStack(CuraContainerStack):
def getNextStack(self) -> Optional["GlobalStack"]:
return super().getNextStack()
def setEnabled(self, enabled):
def setEnabled(self, enabled: bool) -> None:
if "enabled" not in self._metadata:
self.addMetaDataEntry("enabled", "True")
self.setMetaDataEntry("enabled", str(enabled))
self.enabledChanged.emit()
@pyqtProperty(bool, notify = enabledChanged)
def isEnabled(self):
def isEnabled(self) -> bool:
return parseBool(self.getMetaDataEntry("enabled", "True"))
@classmethod
@ -113,7 +114,7 @@ class ExtruderStack(CuraContainerStack):
limit_to_extruder = super().getProperty(key, "limit_to_extruder", context)
if limit_to_extruder is not None:
if limit_to_extruder == -1:
limit_to_extruder = int(Application.getInstance().getMachineManager().defaultExtruderPosition)
limit_to_extruder = int(cura.CuraApplication.CuraApplication.getInstance().getMachineManager().defaultExtruderPosition)
limit_to_extruder = str(limit_to_extruder)
if (limit_to_extruder is not None and limit_to_extruder != "-1") and self.getMetaDataEntry("position") != str(limit_to_extruder):
if str(limit_to_extruder) in self.getNextStack().extruders:
@ -142,7 +143,7 @@ class ExtruderStack(CuraContainerStack):
if stacks:
self.setNextStack(stacks[0])
def _onPropertiesChanged(self, key, properties):
def _onPropertiesChanged(self, key: str, properties: Dict[str, Any]) -> None:
# When there is a setting that is not settable per extruder that depends on a value from a setting that is,
# we do not always get properly informed that we should re-evaluate the setting. So make sure to indicate
# something changed for those settings.

View file

@ -1,29 +1,30 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from collections import defaultdict
import threading
from typing import Any, Dict, Optional
from typing import Any, Dict, Optional, Set, TYPE_CHECKING
from PyQt5.QtCore import pyqtProperty
from UM.Application import Application
from UM.Decorators import override
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.SettingInstance import InstanceState
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.Interfaces import PropertyEvaluationContext
from UM.Logger import Logger
import cura.CuraApplication
from . import Exceptions
from .CuraContainerStack import CuraContainerStack
if TYPE_CHECKING:
from cura.Settings.ExtruderStack import ExtruderStack
## Represents the Global or Machine stack and its related containers.
#
class GlobalStack(CuraContainerStack):
def __init__(self, container_id: str):
def __init__(self, container_id: str) -> None:
super().__init__(container_id)
self.addMetaDataEntry("type", "machine") # For backward compatibility
@ -34,7 +35,7 @@ class GlobalStack(CuraContainerStack):
# and if so, to bypass the resolve to prevent an infinite recursion that would occur
# if the resolve function tried to access the same property it is a resolve for.
# Per thread we have our own resolving_settings, or strange things sometimes occur.
self._resolving_settings = defaultdict(set) # keys are thread names
self._resolving_settings = defaultdict(set) #type: Dict[str, Set[str]] # keys are thread names
## Get the list of extruders of this stack.
#
@ -94,6 +95,7 @@ class GlobalStack(CuraContainerStack):
context.pushContainer(self)
# Handle the "resolve" property.
#TODO: Why the hell does this involve threading?
if self._shouldResolve(key, property_name, context):
current_thread = threading.current_thread()
self._resolving_settings[current_thread.name].add(key)
@ -106,7 +108,7 @@ class GlobalStack(CuraContainerStack):
limit_to_extruder = super().getProperty(key, "limit_to_extruder", context)
if limit_to_extruder is not None:
if limit_to_extruder == -1:
limit_to_extruder = int(Application.getInstance().getMachineManager().defaultExtruderPosition)
limit_to_extruder = int(cura.CuraApplication.CuraApplication.getInstance().getMachineManager().defaultExtruderPosition)
limit_to_extruder = str(limit_to_extruder)
if limit_to_extruder is not None and limit_to_extruder != "-1" and limit_to_extruder in self._extruders:
if super().getProperty(key, "settable_per_extruder", context):
@ -155,7 +157,7 @@ class GlobalStack(CuraContainerStack):
## Perform some sanity checks on the global stack
# Sanity check for extruders; they must have positions 0 and up to machine_extruder_count - 1
def isValid(self):
def isValid(self) -> bool:
container_registry = ContainerRegistry.getInstance()
extruder_trains = container_registry.findContainerStacks(type = "extruder_train", machine = self.getId())

View file

@ -1,10 +1,9 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import collections
import time
#Type hinting.
from typing import List, Dict, TYPE_CHECKING, Optional
from typing import Any, Callable, List, Dict, TYPE_CHECKING, Optional
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
@ -15,20 +14,22 @@ from UM.Signal import Signal
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QTimer
from UM.FlameProfiler import pyqtSlot
from UM import Util
from UM.Application import Application
from UM.Logger import Logger
from UM.Message import Message
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.SettingFunction import SettingFunction
from UM.Signal import postponeSignals, CompressTechnique
import cura.CuraApplication
from cura.Machines.ContainerNode import ContainerNode #For typing.
from cura.Machines.QualityChangesGroup import QualityChangesGroup #For typing.
from cura.Machines.QualityGroup import QualityGroup #For typing.
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
from cura.PrinterOutputDevice import PrinterOutputDevice
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
from cura.Settings.ExtruderManager import ExtruderManager
from cura.Settings.ExtruderStack import ExtruderStack
@ -40,29 +41,31 @@ catalog = i18nCatalog("cura")
if TYPE_CHECKING:
from cura.Settings.CuraContainerStack import CuraContainerStack
from cura.Settings.GlobalStack import GlobalStack
from cura.Machines.MaterialManager import MaterialManager
from cura.Machines.QualityManager import QualityManager
from cura.Machines.VariantManager import VariantManager
class MachineManager(QObject):
def __init__(self, parent = None):
def __init__(self, parent: QObject = None) -> None:
super().__init__(parent)
self._active_container_stack = None # type: Optional[ExtruderManager]
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
self.machine_extruder_material_update_dict = collections.defaultdict(list)
self.machine_extruder_material_update_dict = collections.defaultdict(list) #type: Dict[str, List[Callable[[], None]]]
self._instance_container_timer = QTimer()
self._instance_container_timer = QTimer() #type: QTimer
self._instance_container_timer.setInterval(250)
self._instance_container_timer.setSingleShot(True)
self._instance_container_timer.timeout.connect(self.__emitChangedSignals)
self._application = Application.getInstance()
self._application = cura.CuraApplication.CuraApplication.getInstance() #type: cura.CuraApplication.CuraApplication
self._application.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
self._application.getContainerRegistry().containerLoadComplete.connect(self._onContainersChanged)
@ -74,14 +77,14 @@ class MachineManager(QObject):
self.globalContainerChanged.connect(self.activeQualityChangesGroupChanged)
self.globalContainerChanged.connect(self.activeQualityGroupChanged)
self._stacks_have_errors = None # type:Optional[bool]
self._stacks_have_errors = None # type: Optional[bool]
self._empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
self._empty_definition_changes_container = ContainerRegistry.getInstance().findContainers(id = "empty_definition_changes")[0]
self._empty_variant_container = ContainerRegistry.getInstance().findContainers(id = "empty_variant")[0]
self._empty_material_container = ContainerRegistry.getInstance().findContainers(id = "empty_material")[0]
self._empty_quality_container = ContainerRegistry.getInstance().findContainers(id = "empty_quality")[0]
self._empty_quality_changes_container = ContainerRegistry.getInstance().findContainers(id = "empty_quality_changes")[0]
self._empty_container = CuraContainerRegistry.getInstance().getEmptyInstanceContainer() #type: InstanceContainer
self._empty_definition_changes_container = CuraContainerRegistry.getInstance().findContainers(id = "empty_definition_changes")[0] #type: InstanceContainer
self._empty_variant_container = CuraContainerRegistry.getInstance().findContainers(id = "empty_variant")[0] #type: InstanceContainer
self._empty_material_container = CuraContainerRegistry.getInstance().findContainers(id = "empty_material")[0] #type: InstanceContainer
self._empty_quality_container = CuraContainerRegistry.getInstance().findContainers(id = "empty_quality")[0] #type: InstanceContainer
self._empty_quality_changes_container = CuraContainerRegistry.getInstance().findContainers(id = "empty_quality_changes")[0] #type: InstanceContainer
self._onGlobalContainerChanged()
@ -99,8 +102,6 @@ class MachineManager(QObject):
self._application.getPreferences().addPreference("cura/active_machine", "")
self._global_event_keys = set()
self._printer_output_devices = [] # type: List[PrinterOutputDevice]
self._application.getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
# There might already be some output devices by the time the signal is connected
@ -116,15 +117,15 @@ class MachineManager(QObject):
self._material_incompatible_message = Message(catalog.i18nc("@info:status",
"The selected material is incompatible with the selected machine or configuration."),
title = catalog.i18nc("@info:title", "Incompatible Material"))
title = catalog.i18nc("@info:title", "Incompatible Material")) #type: Message
containers = ContainerRegistry.getInstance().findInstanceContainers(id = self.activeMaterialId)
containers = CuraContainerRegistry.getInstance().findInstanceContainers(id = self.activeMaterialId) #type: List[InstanceContainer]
if containers:
containers[0].nameChanged.connect(self._onMaterialNameChanged)
self._material_manager = self._application.getMaterialManager()
self._variant_manager = self._application.getVariantManager()
self._quality_manager = self._application.getQualityManager()
self._material_manager = self._application.getMaterialManager() #type: MaterialManager
self._variant_manager = self._application.getVariantManager() #type: VariantManager
self._quality_manager = self._application.getQualityManager() #type: QualityManager
# When the materials lookup table gets updated, it can mean that a material has its name changed, which should
# be reflected on the GUI. This signal emission makes sure that it happens.
@ -164,7 +165,7 @@ class MachineManager(QObject):
def setInitialActiveMachine(self) -> None:
active_machine_id = self._application.getPreferences().getValue("cura/active_machine")
if active_machine_id != "" and ContainerRegistry.getInstance().findContainerStacksMetadata(id = active_machine_id):
if active_machine_id != "" and CuraContainerRegistry.getInstance().findContainerStacksMetadata(id = active_machine_id):
# An active machine was saved, so restore it.
self.setActiveMachine(active_machine_id)
@ -215,7 +216,10 @@ class MachineManager(QObject):
@pyqtProperty(int, constant=True)
def totalNumberOfSettings(self) -> int:
return len(ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0].getAllKeys())
general_definition_containers = CuraContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")
if not general_definition_containers:
return 0
return len(general_definition_containers[0].getAllKeys())
def _onGlobalContainerChanged(self) -> None:
if self._global_container_stack:
@ -355,7 +359,7 @@ class MachineManager(QObject):
def setActiveMachine(self, stack_id: str) -> None:
self.blurSettings.emit() # Ensure no-one has focus.
container_registry = ContainerRegistry.getInstance()
container_registry = CuraContainerRegistry.getInstance()
containers = container_registry.findContainerStacks(id = stack_id)
if not containers:
@ -381,7 +385,7 @@ class MachineManager(QObject):
# \param metadata_filter \type{dict} list of metadata keys and values used for filtering
@staticmethod
def getMachine(definition_id: str, metadata_filter: Dict[str, str] = None) -> Optional["GlobalStack"]:
machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
machines = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
for machine in machines:
if machine.definition.getId() == definition_id:
return machine
@ -625,11 +629,13 @@ class MachineManager(QObject):
## Check if a container is read_only
@pyqtSlot(str, result = bool)
def isReadOnly(self, container_id: str) -> bool:
return ContainerRegistry.getInstance().isReadOnly(container_id)
return CuraContainerRegistry.getInstance().isReadOnly(container_id)
## 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())]
@ -641,6 +647,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:
@ -704,7 +712,7 @@ class MachineManager(QObject):
@pyqtSlot(str, str)
def renameMachine(self, machine_id: str, new_name: str) -> None:
container_registry = ContainerRegistry.getInstance()
container_registry = CuraContainerRegistry.getInstance()
machine_stack = container_registry.findContainerStacks(id = machine_id)
if machine_stack:
new_name = container_registry.createUniqueName("machine", machine_stack[0].getName(), new_name, machine_stack[0].definition.getName())
@ -718,23 +726,23 @@ class MachineManager(QObject):
# activate a new machine before removing a machine because this is safer
if activate_new_machine:
machine_stacks = ContainerRegistry.getInstance().findContainerStacksMetadata(type = "machine")
machine_stacks = CuraContainerRegistry.getInstance().findContainerStacksMetadata(type = "machine")
other_machine_stacks = [s for s in machine_stacks if s["id"] != machine_id]
if other_machine_stacks:
self.setActiveMachine(other_machine_stacks[0]["id"])
metadata = ContainerRegistry.getInstance().findContainerStacksMetadata(id = machine_id)[0]
metadata = CuraContainerRegistry.getInstance().findContainerStacksMetadata(id = machine_id)[0]
network_key = metadata["um_network_key"] if "um_network_key" in metadata else None
ExtruderManager.getInstance().removeMachineExtruders(machine_id)
containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(type = "user", machine = machine_id)
containers = CuraContainerRegistry.getInstance().findInstanceContainersMetadata(type = "user", machine = machine_id)
for container in containers:
ContainerRegistry.getInstance().removeContainer(container["id"])
ContainerRegistry.getInstance().removeContainer(machine_id)
CuraContainerRegistry.getInstance().removeContainer(container["id"])
CuraContainerRegistry.getInstance().removeContainer(machine_id)
# If the printer that is being removed is a network printer, the hidden printers have to be also removed
if network_key:
metadata_filter = {"um_network_key": network_key}
hidden_containers = ContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
hidden_containers = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
if hidden_containers:
# This reuses the method and remove all printers recursively
self.removeMachine(hidden_containers[0].getId())
@ -802,14 +810,17 @@ class MachineManager(QObject):
## Get the Definition ID of a machine (specified by ID)
# \param machine_id string machine id to get the definition ID of
# \returns DefinitionID (string) if found, None otherwise
# \returns DefinitionID if found, None otherwise
@pyqtSlot(str, result = str)
def getDefinitionByMachineId(self, machine_id: str) -> str:
containers = ContainerRegistry.getInstance().findContainerStacks(id = machine_id)
def getDefinitionByMachineId(self, machine_id: str) -> Optional[str]:
containers = CuraContainerRegistry.getInstance().findContainerStacks(id = machine_id)
if containers:
return containers[0].definition.getId()
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():
@ -834,6 +845,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)
@ -851,6 +864,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
@ -869,7 +884,7 @@ class MachineManager(QObject):
# Check to see if any objects are set to print with an extruder that will no longer exist
root_node = self._application.getController().getScene().getRoot()
for node in DepthFirstIterator(root_node):
for node in DepthFirstIterator(root_node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
if node.getMeshData():
extruder_nr = node.callDecoration("getActiveExtruderPosition")
@ -884,7 +899,7 @@ class MachineManager(QObject):
global_user_container = self._global_container_stack.userChanges
# Make sure extruder_stacks exists
extruder_stacks = []
extruder_stacks = [] #type: List[ExtruderStack]
if previous_extruder_count == 1:
extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
@ -912,6 +927,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"
@ -924,6 +941,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
@ -936,6 +955,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)
@ -945,6 +966,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()):
@ -954,8 +977,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()
@ -991,6 +1015,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)
@ -999,6 +1025,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)
@ -1041,6 +1069,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
@ -1052,12 +1082,14 @@ class MachineManager(QObject):
self.activeQualityGroupChanged.emit()
self.activeQualityChangesGroupChanged.emit()
def _setQualityGroup(self, quality_group, empty_quality_changes: bool = True) -> None:
def _setQualityGroup(self, quality_group: Optional[QualityGroup], empty_quality_changes: bool = True) -> None:
if self._global_container_stack is None:
return
if quality_group is None:
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:
@ -1081,14 +1113,15 @@ class MachineManager(QObject):
self.activeQualityGroupChanged.emit()
self.activeQualityChangesGroupChanged.emit()
def _fixQualityChangesGroupToNotSupported(self, quality_changes_group):
def _fixQualityChangesGroupToNotSupported(self, quality_changes_group: QualityChangesGroup) -> None:
nodes = [quality_changes_group.node_for_global] + list(quality_changes_group.nodes_for_extruders.values())
containers = [n.getContainer() for n in nodes if n is not None]
for container in containers:
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):
def _setQualityChangesGroup(self, quality_changes_group: QualityChangesGroup) -> None:
if self._global_container_stack is None:
return #Can't change that.
quality_type = quality_changes_group.quality_type
@ -1132,21 +1165,25 @@ class MachineManager(QObject):
self.activeQualityGroupChanged.emit()
self.activeQualityChangesGroupChanged.emit()
def _setVariantNode(self, position, container_node):
if container_node.getContainer() is None:
def _setVariantNode(self, position: str, container_node: ContainerNode) -> None:
if container_node.getContainer() is None or self._global_container_stack is None:
return
self._global_container_stack.extruders[position].variant = container_node.getContainer()
self.activeVariantChanged.emit()
def _setGlobalVariant(self, container_node):
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, container_node = None):
def _setMaterial(self, position: str, container_node: ContainerNode = None) -> None:
if self._global_container_stack is None:
return
if container_node and container_node.getContainer():
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
@ -1155,18 +1192,19 @@ class MachineManager(QObject):
self._current_root_material_id[position] = root_material_id
self.rootMaterialChanged.emit()
def activeMaterialsCompatible(self):
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
def _updateQualityWithMaterial(self, *args):
def _updateQualityWithMaterial(self, *args: Any) -> None:
if self._global_container_stack is None:
return
Logger.log("i", "Updating quality/quality_changes due to material change")
@ -1205,7 +1243,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]):
def updateMaterialWithVariant(self, position: Optional[str]) -> None:
if self._global_container_stack is None:
return
if position is None:
@ -1213,8 +1251,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
@ -1232,28 +1270,28 @@ 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 = ContainerRegistry.getInstance().findDefinitionContainers(name = machine_name)[0].getId()
machine_definition_id = CuraContainerRegistry.getInstance().findDefinitionContainers(name = machine_name)[0].getId()
# Try to find a machine with the same network key
new_machine = self.getMachine(machine_definition_id, metadata_filter = {"um_network_key": self.activeMachineNetworkKey})
# If there is no machine, then create a new one and set it to the non-hidden instance
@ -1275,6 +1313,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)
@ -1309,7 +1349,7 @@ class MachineManager(QObject):
## Find all container stacks that has the pair 'key = value' in its metadata and replaces the value with 'new_value'
def replaceContainersMetadata(self, key: str, value: str, new_value: str) -> None:
machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine")
machines = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine")
for machine in machines:
if machine.getMetaDataEntry(key) == value:
machine.setMetaDataEntry(key, new_value)
@ -1322,18 +1362,18 @@ class MachineManager(QObject):
# Check if the connect_group_name is correct. If not, update all the containers connected to the same printer
if self.activeMachineNetworkGroupName != group_name:
metadata_filter = {"um_network_key": self.activeMachineNetworkKey}
containers = ContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
containers = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
for container in containers:
container.setMetaDataEntry("connect_group_name", group_name)
## This method checks if there is an instance connected to the given network_key
def existNetworkInstances(self, network_key: str) -> bool:
metadata_filter = {"um_network_key": network_key}
containers = ContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
containers = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
return bool(containers)
@pyqtSlot("QVariant")
def setGlobalVariant(self, container_node):
def setGlobalVariant(self, container_node: ContainerNode) -> None:
self.blurSettings.emit()
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self._setGlobalVariant(container_node)
@ -1341,7 +1381,9 @@ class MachineManager(QObject):
self._updateQualityWithMaterial()
@pyqtSlot(str, str)
def setMaterialById(self, position, root_material_id):
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]
@ -1364,12 +1406,14 @@ 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)
@pyqtSlot(str, "QVariant")
def setVariant(self, position: str, container_node):
def setVariant(self, position: str, container_node: ContainerNode) -> None:
position = str(position)
self.blurSettings.emit()
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
@ -1391,7 +1435,7 @@ class MachineManager(QObject):
self.setQualityGroup(quality_group)
@pyqtSlot(QObject)
def setQualityGroup(self, quality_group, no_dialog = False):
def setQualityGroup(self, quality_group: QualityGroup, no_dialog: bool = False) -> None:
self.blurSettings.emit()
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self._setQualityGroup(quality_group)
@ -1401,11 +1445,11 @@ class MachineManager(QObject):
self._application.discardOrKeepProfileChanges()
@pyqtProperty(QObject, fset = setQualityGroup, notify = activeQualityGroupChanged)
def activeQualityGroup(self):
def activeQualityGroup(self) -> Optional[QualityGroup]:
return self._current_quality_group
@pyqtSlot(QObject)
def setQualityChangesGroup(self, quality_changes_group, no_dialog = False):
def setQualityChangesGroup(self, quality_changes_group: QualityChangesGroup, no_dialog: bool = False) -> None:
self.blurSettings.emit()
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self._setQualityChangesGroup(quality_changes_group)
@ -1415,18 +1459,20 @@ class MachineManager(QObject):
self._application.discardOrKeepProfileChanges()
@pyqtSlot()
def resetToUseDefaultQuality(self):
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):
def activeQualityChangesGroup(self) -> Optional[QualityChangesGroup]:
return self._current_quality_changes_group
@pyqtProperty(str, notify = activeQualityGroupChanged)
def activeQualityOrQualityChangesName(self):
def activeQualityOrQualityChangesName(self) -> str:
name = self._empty_quality_container.getName()
if self._current_quality_changes_group:
name = self._current_quality_changes_group.name

View file

@ -1,3 +1,6 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Any, Optional
from UM.Application import Application
@ -9,7 +12,6 @@ from .CuraContainerStack import CuraContainerStack
class PerObjectContainerStack(CuraContainerStack):
@override(CuraContainerStack)
def getProperty(self, key: str, property_name: str, context: Optional[PropertyEvaluationContext] = None) -> Any:
if context is None:
@ -20,7 +22,7 @@ class PerObjectContainerStack(CuraContainerStack):
# Return the user defined value if present, otherwise, evaluate the value according to the default routine.
if self.getContainer(0).hasProperty(key, property_name):
if self.getContainer(0)._instances[key].state == InstanceState.User:
if self.getContainer(0).getProperty(key, "state") == InstanceState.User:
result = super().getProperty(key, property_name, context)
context.popContainer()
return result
@ -53,13 +55,13 @@ class PerObjectContainerStack(CuraContainerStack):
return result
@override(CuraContainerStack)
def setNextStack(self, stack: CuraContainerStack):
def setNextStack(self, stack: CuraContainerStack) -> None:
super().setNextStack(stack)
# trigger signal to re-evaluate all default settings
for key, instance in self.getContainer(0)._instances.items():
for key in self.getContainer(0).getAllKeys():
# only evaluate default settings
if instance.state != InstanceState.Default:
if self.getContainer(0).getProperty(key, "state") != InstanceState.Default:
continue
self._collectPropertyChanges(key, "value")

View file

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

View file

@ -39,12 +39,12 @@ class SimpleModeSettingsManager(QObject):
global_stack = self._machine_manager.activeMachine
# check user settings in the global stack
user_setting_keys.update(set(global_stack.userChanges.getAllKeys()))
user_setting_keys.update(global_stack.userChanges.getAllKeys())
# check user settings in the extruder stacks
if global_stack.extruders:
for extruder_stack in global_stack.extruders.values():
user_setting_keys.update(set(extruder_stack.userChanges.getAllKeys()))
user_setting_keys.update(extruder_stack.userChanges.getAllKeys())
# remove settings that are visible in recommended (we don't show the reset button for those)
for skip_key in self.__ignored_custom_setting_keys:
@ -70,12 +70,12 @@ class SimpleModeSettingsManager(QObject):
global_stack = self._machine_manager.activeMachine
# check quality changes settings in the global stack
quality_changes_keys.update(set(global_stack.qualityChanges.getAllKeys()))
quality_changes_keys.update(global_stack.qualityChanges.getAllKeys())
# check quality changes settings in the extruder stacks
if global_stack.extruders:
for extruder_stack in global_stack.extruders.values():
quality_changes_keys.update(set(extruder_stack.qualityChanges.getAllKeys()))
quality_changes_keys.update(extruder_stack.qualityChanges.getAllKeys())
# check if the qualityChanges container is not empty (meaning it is a user created profile)
has_quality_changes = len(quality_changes_keys) > 0

View file

@ -7,12 +7,12 @@ from typing import List, Optional
from PyQt5.QtNetwork import QLocalServer, QLocalSocket
from UM.Qt.QtApplication import QtApplication #For typing.
from UM.Logger import Logger
class SingleInstance:
def __init__(self, application, files_to_open: Optional[List[str]]):
def __init__(self, application: QtApplication, files_to_open: Optional[List[str]]) -> None:
self._application = application
self._files_to_open = files_to_open
@ -61,17 +61,22 @@ 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):
def _onClientConnected(self) -> None:
Logger.log("i", "New connection recevied on our single-instance server")
connection = self._single_instance_server.nextPendingConnection()
connection = None #type: Optional[QLocalSocket]
if self._single_instance_server:
connection = self._single_instance_server.nextPendingConnection()
if connection is not None:
connection.readyRead.connect(lambda c = connection: self.__readCommands(c))
def __readCommands(self, connection):
def __readCommands(self, connection: QLocalSocket) -> None:
line = connection.readLine()
while len(line) != 0: # There is also a .canReadLine()
try:

View file

@ -134,11 +134,4 @@ import Arcus #@UnusedImport
from cura.CuraApplication import CuraApplication
app = CuraApplication()
app.addCommandLineOptions()
app.parseCliOptions()
app.initialize()
app.startSplashWindowPhase()
app.startPostSplashWindowPhase()
app.run()

View file

@ -1,6 +1,7 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional
import os.path
import zipfile
@ -37,8 +38,8 @@ except ImportError:
## Base implementation for reading 3MF files. Has no support for textures. Only loads meshes!
class ThreeMFReader(MeshReader):
def __init__(self, application):
super().__init__(application)
def __init__(self) -> None:
super().__init__()
MimeTypeDatabase.addMimeType(
MimeType(
@ -168,6 +169,8 @@ class ThreeMFReader(MeshReader):
archive = zipfile.ZipFile(file_name, "r")
self._base_name = os.path.basename(file_name)
parser = Savitar.ThreeMFParser()
with open("/tmp/test.xml", "wb") as f:
f.write(archive.open("3D/3dmodel.model").read())
scene_3mf = parser.parse(archive.open("3D/3dmodel.model").read())
self._unit = scene_3mf.getUnit()
for node in scene_3mf.getSceneNodes():
@ -198,9 +201,9 @@ class ThreeMFReader(MeshReader):
# Second step: 3MF defines the left corner of the machine as center, whereas cura uses the center of the
# build volume.
if global_container_stack:
translation_vector = Vector(x=-global_container_stack.getProperty("machine_width", "value") / 2,
y=-global_container_stack.getProperty("machine_depth", "value") / 2,
z=0)
translation_vector = Vector(x = -global_container_stack.getProperty("machine_width", "value") / 2,
y = -global_container_stack.getProperty("machine_depth", "value") / 2,
z = 0)
translation_matrix = Matrix()
translation_matrix.setByTranslation(translation_vector)
transformation_matrix.multiply(translation_matrix)
@ -236,23 +239,20 @@ class ThreeMFReader(MeshReader):
# * inch
# * foot
# * meter
def _getScaleFromUnit(self, unit):
def _getScaleFromUnit(self, unit: Optional[str]) -> Vector:
conversion_to_mm = {
"micron": 0.001,
"millimeter": 1,
"centimeter": 10,
"meter": 1000,
"inch": 25.4,
"foot": 304.8
}
if unit is None:
unit = "millimeter"
if unit == "micron":
scale = 0.001
elif unit == "millimeter":
scale = 1
elif unit == "centimeter":
scale = 10
elif unit == "inch":
scale = 25.4
elif unit == "foot":
scale = 304.8
elif unit == "meter":
scale = 1000
else:
Logger.log("w", "Unrecognised unit %s used. Assuming mm instead", unit)
scale = 1
elif unit not in conversion_to_mm:
Logger.log("w", "Unrecognised unit {unit} used. Assuming mm instead.".format(unit = unit))
unit = "millimeter"
scale = conversion_to_mm[unit]
return Vector(scale, scale, scale)

View file

@ -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
@ -38,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
@ -47,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
@ -66,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
@ -82,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(
@ -112,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]
@ -671,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":

View file

@ -187,7 +187,10 @@ class WorkspaceDialog(QObject):
@pyqtProperty(int, constant = True)
def totalNumberOfSettings(self):
return len(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0].getAllKeys())
general_definition_containers = ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")
if not general_definition_containers:
return 0
return len(general_definition_containers[0].getAllKeys())
@pyqtProperty(int, notify = numVisibleSettingsChanged)
def numVisibleSettings(self):

View file

@ -18,7 +18,7 @@ catalog = i18nCatalog("cura")
def getMetaData() -> Dict:
# Workarround for osx not supporting double file extensions correctly.
# Workaround for osx not supporting double file extensions correctly.
if Platform.isOSX():
workspace_extension = "3mf"
else:
@ -44,7 +44,7 @@ def getMetaData() -> Dict:
def register(app):
if "3MFReader.ThreeMFReader" in sys.modules:
return {"mesh_reader": ThreeMFReader.ThreeMFReader(app),
return {"mesh_reader": ThreeMFReader.ThreeMFReader(),
"workspace_reader": ThreeMFWorkspaceReader.ThreeMFWorkspaceReader()}
else:
return {}

View file

@ -91,7 +91,7 @@ class ThreeMFWriter(MeshWriter):
# Handle per object settings (if any)
stack = um_node.callDecoration("getStack")
if stack is not None:
changed_setting_keys = set(stack.getTop().getAllKeys())
changed_setting_keys = stack.getTop().getAllKeys()
# Ensure that we save the extruder used for this object in a multi-extrusion setup
if stack.getProperty("machine_extruder_count", "value") > 1:

View file

@ -1,8 +1,14 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from collections import defaultdict
import os
from PyQt5.QtCore import QObject, QTimer, pyqtSlot
import sys
from time import time
from typing import Any, cast, Dict, List, Optional, Set, TYPE_CHECKING
from UM.Backend.Backend import Backend, BackendState
from UM.Application import Application
from UM.Scene.SceneNode import SceneNode
from UM.Signal import Signal
from UM.Logger import Logger
@ -10,29 +16,30 @@ from UM.Message import Message
from UM.PluginRegistry import PluginRegistry
from UM.Resources import Resources
from UM.Platform import Platform
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Qt.Duration import DurationFormat
from PyQt5.QtCore import QObject, pyqtSlot
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Settings.Interfaces import DefinitionContainerInterface
from UM.Settings.SettingInstance import SettingInstance #For typing.
from UM.Tool import Tool #For typing.
from collections import defaultdict
from cura.CuraApplication import CuraApplication
from cura.Settings.ExtruderManager import ExtruderManager
from . import ProcessSlicedLayersJob
from . import StartSliceJob
import os
import sys
from time import time
from PyQt5.QtCore import QTimer
from .ProcessSlicedLayersJob import ProcessSlicedLayersJob
from .StartSliceJob import StartSliceJob, StartJobResult
import Arcus
if TYPE_CHECKING:
from cura.Machines.Models.MultiBuildPlateModel import MultiBuildPlateModel
from cura.Machines.MachineErrorChecker import MachineErrorChecker
from UM.Scene.Scene import Scene
from UM.Settings.ContainerStack import ContainerStack
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
class CuraEngineBackend(QObject, Backend):
backendError = Signal()
## Starts the back-end plug-in.
@ -40,16 +47,16 @@ class CuraEngineBackend(QObject, Backend):
# This registers all the signal listeners and prepares for communication
# with the back-end in general.
# CuraEngineBackend is exposed to qml as well.
def __init__(self, parent = None):
super().__init__(parent = parent)
def __init__(self) -> None:
super().__init__()
# Find out where the engine is located, and how it is called.
# This depends on how Cura is packaged and which OS we are running on.
executable_name = "CuraEngine"
if Platform.isWindows():
executable_name += ".exe"
default_engine_location = executable_name
if os.path.exists(os.path.join(Application.getInstallPrefix(), "bin", executable_name)):
default_engine_location = os.path.join(Application.getInstallPrefix(), "bin", executable_name)
if os.path.exists(os.path.join(CuraApplication.getInstallPrefix(), "bin", executable_name)):
default_engine_location = os.path.join(CuraApplication.getInstallPrefix(), "bin", executable_name)
if hasattr(sys, "frozen"):
default_engine_location = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), executable_name)
if Platform.isLinux() and not default_engine_location:
@ -61,9 +68,9 @@ class CuraEngineBackend(QObject, Backend):
default_engine_location = execpath
break
self._application = Application.getInstance()
self._multi_build_plate_model = None
self._machine_error_checker = None
self._application = CuraApplication.getInstance() #type: CuraApplication
self._multi_build_plate_model = None #type: MultiBuildPlateModel
self._machine_error_checker = None #type: MachineErrorChecker
if not default_engine_location:
raise EnvironmentError("Could not find CuraEngine")
@ -71,16 +78,16 @@ class CuraEngineBackend(QObject, Backend):
Logger.log("i", "Found CuraEngine at: %s", default_engine_location)
default_engine_location = os.path.abspath(default_engine_location)
Application.getInstance().getPreferences().addPreference("backend/location", default_engine_location)
self._application.getPreferences().addPreference("backend/location", default_engine_location)
# Workaround to disable layer view processing if layer view is not active.
self._layer_view_active = False
self._layer_view_active = False #type: bool
self._onActiveViewChanged()
self._stored_layer_data = []
self._stored_optimized_layer_data = {} # key is build plate number, then arrays are stored until they go to the ProcessSlicesLayersJob
self._stored_layer_data = [] #type: List[Arcus.PythonMessage]
self._stored_optimized_layer_data = {} #type: Dict[int, List[Arcus.PythonMessage]] # key is build plate number, then arrays are stored until they go to the ProcessSlicesLayersJob
self._scene = self._application.getController().getScene()
self._scene = self._application.getController().getScene() #type: Scene
self._scene.sceneChanged.connect(self._onSceneChanged)
# Triggers for auto-slicing. Auto-slicing is triggered as follows:
@ -91,7 +98,7 @@ class CuraEngineBackend(QObject, Backend):
# If there is an error check, stop the auto-slicing timer, and only wait for the error check to be finished
# to start the auto-slicing timer again.
#
self._global_container_stack = None
self._global_container_stack = None #type: Optional[ContainerStack]
# Listeners for receiving messages from the back-end.
self._message_handlers["cura.proto.Layer"] = self._onLayerMessage
@ -102,39 +109,39 @@ class CuraEngineBackend(QObject, Backend):
self._message_handlers["cura.proto.PrintTimeMaterialEstimates"] = self._onPrintTimeMaterialEstimates
self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage
self._start_slice_job = None
self._start_slice_job_build_plate = None
self._slicing = False # Are we currently slicing?
self._restart = False # Back-end is currently restarting?
self._tool_active = False # If a tool is active, some tasks do not have to do anything
self._always_restart = True # Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
self._process_layers_job = None # The currently active job to process layers, or None if it is not processing layers.
self._build_plates_to_be_sliced = [] # what needs slicing?
self._engine_is_fresh = True # Is the newly started engine used before or not?
self._start_slice_job = None #type: Optional[StartSliceJob]
self._start_slice_job_build_plate = None #type: Optional[int]
self._slicing = False #type: bool # Are we currently slicing?
self._restart = False #type: bool # Back-end is currently restarting?
self._tool_active = False #type: bool # If a tool is active, some tasks do not have to do anything
self._always_restart = True #type: bool # Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
self._process_layers_job = None #type: Optional[ProcessSlicedLayersJob] # The currently active job to process layers, or None if it is not processing layers.
self._build_plates_to_be_sliced = [] #type: List[int] # what needs slicing?
self._engine_is_fresh = True #type: bool # Is the newly started engine used before or not?
self._backend_log_max_lines = 20000 # Maximum number of lines to buffer
self._error_message = None # Pop-up message that shows errors.
self._last_num_objects = defaultdict(int) # Count number of objects to see if there is something changed
self._postponed_scene_change_sources = [] # scene change is postponed (by a tool)
self._backend_log_max_lines = 20000 #type: int # Maximum number of lines to buffer
self._error_message = None #type: Message # Pop-up message that shows errors.
self._last_num_objects = defaultdict(int) #type: Dict[int, int] # Count number of objects to see if there is something changed
self._postponed_scene_change_sources = [] #type: List[SceneNode] # scene change is postponed (by a tool)
self._slice_start_time = None
self._is_disabled = False
self._slice_start_time = None #type: Optional[float]
self._is_disabled = False #type: bool
Application.getInstance().getPreferences().addPreference("general/auto_slice", False)
self._application.getPreferences().addPreference("general/auto_slice", False)
self._use_timer = False
self._use_timer = False #type: bool
# When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired.
# This timer will group them up, and only slice for the last setting changed signal.
# TODO: Properly group propertyChanged signals by whether they are triggered by the same user interaction.
self._change_timer = QTimer()
self._change_timer = QTimer() #type: QTimer
self._change_timer.setSingleShot(True)
self._change_timer.setInterval(500)
self.determineAutoSlicing()
Application.getInstance().getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
self._application.getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
self._application.initializationFinished.connect(self.initialize)
def initialize(self):
def initialize(self) -> None:
self._multi_build_plate_model = self._application.getMultiBuildPlateModel()
self._application.getController().activeViewChanged.connect(self._onActiveViewChanged)
@ -160,16 +167,16 @@ class CuraEngineBackend(QObject, Backend):
#
# This function should terminate the engine process.
# Called when closing the application.
def close(self):
def close(self) -> None:
# Terminate CuraEngine if it is still running at this point
self._terminate()
## Get the command that is used to call the engine.
# This is useful for debugging and used to actually start the engine.
# \return list of commands and args / parameters.
def getEngineCommand(self):
def getEngineCommand(self) -> List[str]:
json_path = Resources.getPath(Resources.DefinitionContainers, "fdmprinter.def.json")
return [Application.getInstance().getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, ""]
return [self._application.getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, ""]
## Emitted when we get a message containing print duration and material amount.
# This also implies the slicing has finished.
@ -184,13 +191,13 @@ class CuraEngineBackend(QObject, Backend):
slicingCancelled = Signal()
@pyqtSlot()
def stopSlicing(self):
def stopSlicing(self) -> None:
self.backendStateChange.emit(BackendState.NotStarted)
if self._slicing: # We were already slicing. Stop the old job.
self._terminate()
self._createSocket()
if self._process_layers_job: # We were processing layers. Stop that, the layers are going to change soon.
if self._process_layers_job is not None: # We were processing layers. Stop that, the layers are going to change soon.
Logger.log("d", "Aborting process layers job...")
self._process_layers_job.abort()
self._process_layers_job = None
@ -200,12 +207,12 @@ class CuraEngineBackend(QObject, Backend):
## Manually triggers a reslice
@pyqtSlot()
def forceSlice(self):
def forceSlice(self) -> None:
self.markSliceAll()
self.slice()
## Perform a slice of the scene.
def slice(self):
def slice(self) -> None:
Logger.log("d", "Starting to slice...")
self._slice_start_time = time()
if not self._build_plates_to_be_sliced:
@ -218,10 +225,10 @@ class CuraEngineBackend(QObject, Backend):
return
if not hasattr(self._scene, "gcode_dict"):
self._scene.gcode_dict = {}
self._scene.gcode_dict = {} #type: ignore #Because we are creating the missing attribute here.
# see if we really have to slice
active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
active_build_plate = self._application.getMultiBuildPlateModel().activeBuildPlate
build_plate_to_be_sliced = self._build_plates_to_be_sliced.pop(0)
Logger.log("d", "Going to slice build plate [%s]!" % build_plate_to_be_sliced)
num_objects = self._numObjectsPerBuildPlate()
@ -230,14 +237,14 @@ class CuraEngineBackend(QObject, Backend):
self._stored_optimized_layer_data[build_plate_to_be_sliced] = []
if build_plate_to_be_sliced not in num_objects or num_objects[build_plate_to_be_sliced] == 0:
self._scene.gcode_dict[build_plate_to_be_sliced] = []
self._scene.gcode_dict[build_plate_to_be_sliced] = [] #type: ignore #Because we created this attribute above.
Logger.log("d", "Build plate %s has no objects to be sliced, skipping", build_plate_to_be_sliced)
if self._build_plates_to_be_sliced:
self.slice()
return
if Application.getInstance().getPrintInformation() and build_plate_to_be_sliced == active_build_plate:
Application.getInstance().getPrintInformation().setToZeroPrintInformation(build_plate_to_be_sliced)
if self._application.getPrintInformation() and build_plate_to_be_sliced == active_build_plate:
self._application.getPrintInformation().setToZeroPrintInformation(build_plate_to_be_sliced)
if self._process is None:
self._createSocket()
@ -247,14 +254,14 @@ class CuraEngineBackend(QObject, Backend):
self.processingProgress.emit(0.0)
self.backendStateChange.emit(BackendState.NotStarted)
self._scene.gcode_dict[build_plate_to_be_sliced] = [] #[] indexed by build plate number
self._scene.gcode_dict[build_plate_to_be_sliced] = [] #type: ignore #[] indexed by build plate number
self._slicing = True
self.slicingStarted.emit()
self.determineAutoSlicing() # Switch timer on or off if appropriate
slice_message = self._socket.createMessage("cura.proto.Slice")
self._start_slice_job = StartSliceJob.StartSliceJob(slice_message)
self._start_slice_job = StartSliceJob(slice_message)
self._start_slice_job_build_plate = build_plate_to_be_sliced
self._start_slice_job.setBuildPlate(self._start_slice_job_build_plate)
self._start_slice_job.start()
@ -262,7 +269,7 @@ class CuraEngineBackend(QObject, Backend):
## Terminate the engine process.
# Start the engine process by calling _createSocket()
def _terminate(self):
def _terminate(self) -> None:
self._slicing = False
self._stored_layer_data = []
if self._start_slice_job_build_plate in self._stored_optimized_layer_data:
@ -274,7 +281,7 @@ class CuraEngineBackend(QObject, Backend):
self.processingProgress.emit(0)
Logger.log("d", "Attempting to kill the engine process")
if Application.getInstance().getUseExternalBackend():
if self._application.getUseExternalBackend():
return
if self._process is not None:
@ -295,7 +302,7 @@ class CuraEngineBackend(QObject, Backend):
# bootstrapping of a slice job.
#
# \param job The start slice job that was just finished.
def _onStartSliceCompleted(self, job):
def _onStartSliceCompleted(self, job: StartSliceJob) -> None:
if self._error_message:
self._error_message.hide()
@ -303,13 +310,13 @@ class CuraEngineBackend(QObject, Backend):
if self._start_slice_job is job:
self._start_slice_job = None
if job.isCancelled() or job.getError() or job.getResult() == StartSliceJob.StartJobResult.Error:
if job.isCancelled() or job.getError() or job.getResult() == StartJobResult.Error:
self.backendStateChange.emit(BackendState.Error)
self.backendError.emit(job)
return
if job.getResult() == StartSliceJob.StartJobResult.MaterialIncompatible:
if Application.getInstance().platformActivity:
if job.getResult() == StartJobResult.MaterialIncompatible:
if self._application.platformActivity:
self._error_message = Message(catalog.i18nc("@info:status",
"Unable to slice with the current material as it is incompatible with the selected machine or configuration."), title = catalog.i18nc("@info:title", "Unable to slice"))
self._error_message.show()
@ -319,10 +326,10 @@ class CuraEngineBackend(QObject, Backend):
self.backendStateChange.emit(BackendState.NotStarted)
return
if job.getResult() == StartSliceJob.StartJobResult.SettingError:
if Application.getInstance().platformActivity:
if job.getResult() == StartJobResult.SettingError:
if self._application.platformActivity:
extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
error_keys = []
error_keys = [] #type: List[str]
for extruder in extruders:
error_keys.extend(extruder.getErrorKeys())
if not extruders:
@ -330,7 +337,7 @@ class CuraEngineBackend(QObject, Backend):
error_labels = set()
for key in error_keys:
for stack in [self._global_container_stack] + extruders: #Search all container stacks for the definition of this setting. Some are only in an extruder stack.
definitions = stack.getBottom().findDefinitions(key = key)
definitions = cast(DefinitionContainerInterface, stack.getBottom()).findDefinitions(key = key)
if definitions:
break #Found it! No need to continue search.
else: #No stack has a definition for this setting.
@ -338,8 +345,7 @@ class CuraEngineBackend(QObject, Backend):
continue
error_labels.add(definitions[0].label)
error_labels = ", ".join(error_labels)
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice with the current settings. The following settings have errors: {0}").format(error_labels),
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice with the current settings. The following settings have errors: {0}").format(", ".join(error_labels)),
title = catalog.i18nc("@info:title", "Unable to slice"))
self._error_message.show()
self.backendStateChange.emit(BackendState.Error)
@ -348,29 +354,27 @@ class CuraEngineBackend(QObject, Backend):
self.backendStateChange.emit(BackendState.NotStarted)
return
elif job.getResult() == StartSliceJob.StartJobResult.ObjectSettingError:
elif job.getResult() == StartJobResult.ObjectSettingError:
errors = {}
for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()):
for node in DepthFirstIterator(self._application.getController().getScene().getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
stack = node.callDecoration("getStack")
if not stack:
continue
for key in stack.getErrorKeys():
definition = self._global_container_stack.getBottom().findDefinitions(key = key)
definition = cast(DefinitionContainerInterface, self._global_container_stack.getBottom()).findDefinitions(key = key)
if not definition:
Logger.log("e", "When checking settings for errors, unable to find definition for key {key} in per-object stack.".format(key = key))
continue
definition = definition[0]
errors[key] = definition.label
error_labels = ", ".join(errors.values())
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice due to some per-model settings. The following settings have errors on one or more models: {error_labels}").format(error_labels = error_labels),
errors[key] = definition[0].label
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice due to some per-model settings. The following settings have errors on one or more models: {error_labels}").format(error_labels = ", ".join(errors.values())),
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.BuildPlateError:
if Application.getInstance().platformActivity:
if job.getResult() == StartJobResult.BuildPlateError:
if self._application.platformActivity:
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice because the prime tower or prime position(s) are invalid."),
title = catalog.i18nc("@info:title", "Unable to slice"))
self._error_message.show()
@ -379,7 +383,7 @@ class CuraEngineBackend(QObject, Backend):
else:
self.backendStateChange.emit(BackendState.NotStarted)
if job.getResult() == StartSliceJob.StartJobResult.ObjectsWithDisabledExtruder:
if job.getResult() == 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()
@ -387,8 +391,8 @@ class CuraEngineBackend(QObject, Backend):
self.backendError.emit(job)
return
if job.getResult() == StartSliceJob.StartJobResult.NothingToSlice:
if Application.getInstance().platformActivity:
if job.getResult() == StartJobResult.NothingToSlice:
if self._application.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."),
title = catalog.i18nc("@info:title", "Unable to slice"))
self._error_message.show()
@ -411,20 +415,20 @@ class CuraEngineBackend(QObject, Backend):
# It disables when
# - preference auto slice is off
# - decorator isBlockSlicing is found (used in g-code reader)
def determineAutoSlicing(self):
def determineAutoSlicing(self) -> bool:
enable_timer = True
self._is_disabled = False
if not Application.getInstance().getPreferences().getValue("general/auto_slice"):
if not self._application.getPreferences().getValue("general/auto_slice"):
enable_timer = False
for node in DepthFirstIterator(self._scene.getRoot()):
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
if node.callDecoration("isBlockSlicing"):
enable_timer = False
self.backendStateChange.emit(BackendState.Disabled)
self._is_disabled = True
gcode_list = node.callDecoration("getGCodeList")
if gcode_list is not None:
self._scene.gcode_dict[node.callDecoration("getBuildPlateNumber")] = gcode_list
self._scene.gcode_dict[node.callDecoration("getBuildPlateNumber")] = gcode_list #type: ignore #Because we generate this attribute dynamically.
if self._use_timer == enable_timer:
return self._use_timer
@ -437,9 +441,9 @@ class CuraEngineBackend(QObject, Backend):
return False
## Return a dict with number of objects per build plate
def _numObjectsPerBuildPlate(self):
num_objects = defaultdict(int)
for node in DepthFirstIterator(self._scene.getRoot()):
def _numObjectsPerBuildPlate(self) -> Dict[int, int]:
num_objects = defaultdict(int) #type: Dict[int, int]
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
# Only count sliceable objects
if node.callDecoration("isSliceable"):
build_plate_number = node.callDecoration("getBuildPlateNumber")
@ -451,7 +455,7 @@ class CuraEngineBackend(QObject, Backend):
# This should start a slice if the scene is now ready to slice.
#
# \param source The scene node that was changed.
def _onSceneChanged(self, source):
def _onSceneChanged(self, source: SceneNode) -> None:
if not isinstance(source, SceneNode):
return
@ -506,8 +510,8 @@ class CuraEngineBackend(QObject, Backend):
## Called when an error occurs in the socket connection towards the engine.
#
# \param error The exception that occurred.
def _onSocketError(self, error):
if Application.getInstance().isShuttingDown():
def _onSocketError(self, error: Arcus.Error) -> None:
if self._application.isShuttingDown():
return
super()._onSocketError(error)
@ -521,19 +525,19 @@ class CuraEngineBackend(QObject, Backend):
Logger.log("w", "A socket error caused the connection to be reset")
## Remove old layer data (if any)
def _clearLayerData(self, build_plate_numbers = set()):
for node in DepthFirstIterator(self._scene.getRoot()):
def _clearLayerData(self, build_plate_numbers: Set = None) -> None:
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
if node.callDecoration("getLayerData"):
if not build_plate_numbers or node.callDecoration("getBuildPlateNumber") in build_plate_numbers:
node.getParent().removeChild(node)
def markSliceAll(self):
for build_plate_number in range(Application.getInstance().getMultiBuildPlateModel().maxBuildPlate + 1):
def markSliceAll(self) -> None:
for build_plate_number in range(self._application.getMultiBuildPlateModel().maxBuildPlate + 1):
if build_plate_number not in self._build_plates_to_be_sliced:
self._build_plates_to_be_sliced.append(build_plate_number)
## Convenient function: mark everything to slice, emit state and clear layer data
def needsSlicing(self):
def needsSlicing(self) -> None:
self.stopSlicing()
self.markSliceAll()
self.processingProgress.emit(0.0)
@ -545,7 +549,7 @@ class CuraEngineBackend(QObject, Backend):
## A setting has changed, so check if we must reslice.
# \param instance The setting instance that has changed.
# \param property The property of the setting instance that has changed.
def _onSettingChanged(self, instance, property):
def _onSettingChanged(self, instance: SettingInstance, property: str) -> None:
if property == "value": # Only reslice if the value has changed.
self.needsSlicing()
self._onChanged()
@ -554,7 +558,7 @@ class CuraEngineBackend(QObject, Backend):
if self._use_timer:
self._change_timer.stop()
def _onStackErrorCheckFinished(self):
def _onStackErrorCheckFinished(self) -> None:
self.determineAutoSlicing()
if self._is_disabled:
return
@ -566,13 +570,13 @@ class CuraEngineBackend(QObject, Backend):
## Called when a sliced layer data message is received from the engine.
#
# \param message The protobuf message containing sliced layer data.
def _onLayerMessage(self, message):
def _onLayerMessage(self, message: Arcus.PythonMessage) -> None:
self._stored_layer_data.append(message)
## Called when an optimized sliced layer data message is received from the engine.
#
# \param message The protobuf message containing sliced layer data.
def _onOptimizedLayerMessage(self, message):
def _onOptimizedLayerMessage(self, message: Arcus.PythonMessage) -> None:
if self._start_slice_job_build_plate not in self._stored_optimized_layer_data:
self._stored_optimized_layer_data[self._start_slice_job_build_plate] = []
self._stored_optimized_layer_data[self._start_slice_job_build_plate].append(message)
@ -580,11 +584,11 @@ class CuraEngineBackend(QObject, Backend):
## Called when a progress message is received from the engine.
#
# \param message The protobuf message containing the slicing progress.
def _onProgressMessage(self, message):
def _onProgressMessage(self, message: Arcus.PythonMessage) -> None:
self.processingProgress.emit(message.amount)
self.backendStateChange.emit(BackendState.Processing)
def _invokeSlice(self):
def _invokeSlice(self) -> None:
if self._use_timer:
# if the error check is scheduled, wait for the error check finish signal to trigger auto-slice,
# otherwise business as usual
@ -600,17 +604,17 @@ class CuraEngineBackend(QObject, Backend):
## Called when the engine sends a message that slicing is finished.
#
# \param message The protobuf message signalling that slicing is finished.
def _onSlicingFinishedMessage(self, message):
def _onSlicingFinishedMessage(self, message: Arcus.PythonMessage) -> None:
self.backendStateChange.emit(BackendState.Done)
self.processingProgress.emit(1.0)
gcode_list = self._scene.gcode_dict[self._start_slice_job_build_plate]
gcode_list = self._scene.gcode_dict[self._start_slice_job_build_plate] #type: ignore #Because we generate this attribute dynamically.
for index, line in enumerate(gcode_list):
replaced = line.replace("{print_time}", str(Application.getInstance().getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.ISO8601)))
replaced = replaced.replace("{filament_amount}", str(Application.getInstance().getPrintInformation().materialLengths))
replaced = replaced.replace("{filament_weight}", str(Application.getInstance().getPrintInformation().materialWeights))
replaced = replaced.replace("{filament_cost}", str(Application.getInstance().getPrintInformation().materialCosts))
replaced = replaced.replace("{jobname}", str(Application.getInstance().getPrintInformation().jobName))
replaced = line.replace("{print_time}", str(self._application.getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.ISO8601)))
replaced = replaced.replace("{filament_amount}", str(self._application.getPrintInformation().materialLengths))
replaced = replaced.replace("{filament_weight}", str(self._application.getPrintInformation().materialWeights))
replaced = replaced.replace("{filament_cost}", str(self._application.getPrintInformation().materialCosts))
replaced = replaced.replace("{jobname}", str(self._application.getPrintInformation().jobName))
gcode_list[index] = replaced
@ -619,7 +623,7 @@ class CuraEngineBackend(QObject, Backend):
Logger.log("d", "Number of models per buildplate: %s", dict(self._numObjectsPerBuildPlate()))
# See if we need to process the sliced layers job.
active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
active_build_plate = self._application.getMultiBuildPlateModel().activeBuildPlate
if (
self._layer_view_active and
(self._process_layers_job is None or not self._process_layers_job.isRunning()) and
@ -641,25 +645,27 @@ class CuraEngineBackend(QObject, Backend):
## Called when a g-code message is received from the engine.
#
# \param message The protobuf message containing g-code, encoded as UTF-8.
def _onGCodeLayerMessage(self, message):
self._scene.gcode_dict[self._start_slice_job_build_plate].append(message.data.decode("utf-8", "replace"))
def _onGCodeLayerMessage(self, message: Arcus.PythonMessage) -> None:
self._scene.gcode_dict[self._start_slice_job_build_plate].append(message.data.decode("utf-8", "replace")) #type: ignore #Because we generate this attribute dynamically.
## Called when a g-code prefix message is received from the engine.
#
# \param message The protobuf message containing the g-code prefix,
# encoded as UTF-8.
def _onGCodePrefixMessage(self, message):
self._scene.gcode_dict[self._start_slice_job_build_plate].insert(0, message.data.decode("utf-8", "replace"))
def _onGCodePrefixMessage(self, message: Arcus.PythonMessage) -> None:
self._scene.gcode_dict[self._start_slice_job_build_plate].insert(0, message.data.decode("utf-8", "replace")) #type: ignore #Because we generate this attribute dynamically.
## Creates a new socket connection.
def _createSocket(self):
super()._createSocket(os.path.abspath(os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "Cura.proto")))
def _createSocket(self, protocol_file: str = None) -> None:
if not protocol_file:
protocol_file = os.path.abspath(os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "Cura.proto"))
super()._createSocket(protocol_file)
self._engine_is_fresh = True
## Called when anything has changed to the stuff that needs to be sliced.
#
# This indicates that we should probably re-slice soon.
def _onChanged(self, *args, **kwargs):
def _onChanged(self, *args: Any, **kwargs: Any) -> None:
self.needsSlicing()
if self._use_timer:
# if the error check is scheduled, wait for the error check finish signal to trigger auto-slice,
@ -677,7 +683,7 @@ class CuraEngineBackend(QObject, Backend):
#
# \param message The protobuf message containing the print time per feature and
# material amount per extruder
def _onPrintTimeMaterialEstimates(self, message):
def _onPrintTimeMaterialEstimates(self, message: Arcus.PythonMessage) -> None:
material_amounts = []
for index in range(message.repeatedMessageCount("materialEstimates")):
material_amounts.append(message.getRepeatedMessage("materialEstimates", index).material_amount)
@ -688,7 +694,7 @@ class CuraEngineBackend(QObject, Backend):
## Called for parsing message to retrieve estimated time per feature
#
# \param message The protobuf message containing the print time per feature
def _parseMessagePrintTimes(self, message):
def _parseMessagePrintTimes(self, message: Arcus.PythonMessage) -> Dict[str, float]:
result = {
"inset_0": message.time_inset_0,
"inset_x": message.time_inset_x,
@ -705,7 +711,7 @@ class CuraEngineBackend(QObject, Backend):
return result
## Called when the back-end connects to the front-end.
def _onBackendConnected(self):
def _onBackendConnected(self) -> None:
if self._restart:
self._restart = False
self._onChanged()
@ -716,7 +722,7 @@ class CuraEngineBackend(QObject, Backend):
# continuously slicing while the user is dragging some tool handle.
#
# \param tool The tool that the user is using.
def _onToolOperationStarted(self, tool):
def _onToolOperationStarted(self, tool: Tool) -> None:
self._tool_active = True # Do not react on scene change
self.disableTimer()
# Restart engine as soon as possible, we know we want to slice afterwards
@ -729,7 +735,7 @@ class CuraEngineBackend(QObject, Backend):
# This indicates that we can safely start slicing again.
#
# \param tool The tool that the user was using.
def _onToolOperationStopped(self, tool):
def _onToolOperationStopped(self, tool: Tool) -> None:
self._tool_active = False # React on scene change again
self.determineAutoSlicing() # Switch timer on if appropriate
# Process all the postponed scene changes
@ -737,18 +743,17 @@ class CuraEngineBackend(QObject, Backend):
source = self._postponed_scene_change_sources.pop(0)
self._onSceneChanged(source)
def _startProcessSlicedLayersJob(self, build_plate_number):
self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_optimized_layer_data[build_plate_number])
def _startProcessSlicedLayersJob(self, build_plate_number: int) -> None:
self._process_layers_job = ProcessSlicedLayersJob(self._stored_optimized_layer_data[build_plate_number])
self._process_layers_job.setBuildPlate(build_plate_number)
self._process_layers_job.finished.connect(self._onProcessLayersFinished)
self._process_layers_job.start()
## Called when the user changes the active view mode.
def _onActiveViewChanged(self):
application = Application.getInstance()
view = application.getController().getActiveView()
def _onActiveViewChanged(self) -> None:
view = self._application.getController().getActiveView()
if view:
active_build_plate = application.getMultiBuildPlateModel().activeBuildPlate
active_build_plate = self._application.getMultiBuildPlateModel().activeBuildPlate
if view.getPluginId() == "SimulationView": # If switching to layer view, we should process the layers if that hasn't been done yet.
self._layer_view_active = True
# There is data and we're not slicing at the moment
@ -766,14 +771,14 @@ class CuraEngineBackend(QObject, Backend):
## Called when the back-end self-terminates.
#
# We should reset our state and start listening for new connections.
def _onBackendQuit(self):
def _onBackendQuit(self) -> None:
if not self._restart:
if self._process:
Logger.log("d", "Backend quit with return code %s. Resetting process and socket.", self._process.wait())
self._process = None
## Called when the global container stack changes
def _onGlobalStackChanged(self):
def _onGlobalStackChanged(self) -> None:
if self._global_container_stack:
self._global_container_stack.propertyChanged.disconnect(self._onSettingChanged)
self._global_container_stack.containersChanged.disconnect(self._onChanged)
@ -783,7 +788,7 @@ class CuraEngineBackend(QObject, Backend):
extruder.propertyChanged.disconnect(self._onSettingChanged)
extruder.containersChanged.disconnect(self._onChanged)
self._global_container_stack = Application.getInstance().getGlobalContainerStack()
self._global_container_stack = self._application.getGlobalContainerStack()
if self._global_container_stack:
self._global_container_stack.propertyChanged.connect(self._onSettingChanged) # Note: Only starts slicing when the value changed.
@ -794,26 +799,26 @@ class CuraEngineBackend(QObject, Backend):
extruder.containersChanged.connect(self._onChanged)
self._onChanged()
def _onProcessLayersFinished(self, job):
def _onProcessLayersFinished(self, job: ProcessSlicedLayersJob) -> None:
del self._stored_optimized_layer_data[job.getBuildPlate()]
self._process_layers_job = None
Logger.log("d", "See if there is more to slice(2)...")
self._invokeSlice()
## Connect slice function to timer.
def enableTimer(self):
def enableTimer(self) -> None:
if not self._use_timer:
self._change_timer.timeout.connect(self.slice)
self._use_timer = True
## Disconnect slice function from timer.
# This means that slicing will not be triggered automatically
def disableTimer(self):
def disableTimer(self) -> None:
if self._use_timer:
self._use_timer = False
self._change_timer.timeout.disconnect(self.slice)
def _onPreferencesChanged(self, preference):
def _onPreferencesChanged(self, preference: str) -> None:
if preference != "general/auto_slice":
return
auto_slice = self.determineAutoSlicing()
@ -821,11 +826,11 @@ class CuraEngineBackend(QObject, Backend):
self._change_timer.start()
## Tickle the backend so in case of auto slicing, it starts the timer.
def tickle(self):
def tickle(self) -> None:
if self._use_timer:
self._change_timer.start()
def _extruderChanged(self):
def _extruderChanged(self) -> None:
for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1):
if build_plate_number not in self._build_plates_to_be_sliced:
self._build_plates_to_be_sliced.append(build_plate_number)

View file

@ -1,21 +1,25 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import numpy
from string import Formatter
from enum import IntEnum
import time
from typing import Any, Dict, List, Optional, Set
import re
import Arcus #For typing.
from UM.Job import Job
from UM.Application import Application
from UM.Logger import Logger
from UM.Settings.ContainerStack import ContainerStack #For typing.
from UM.Settings.SettingRelation import SettingRelation #For typing.
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.Scene import Scene #For typing.
from UM.Settings.Validator import ValidatorState
from UM.Settings.SettingRelation import RelationType
from cura.CuraApplication import CuraApplication
from cura.Scene.CuraSceneNode import CuraSceneNode
from cura.OneAtATimeIterator import OneAtATimeIterator
from cura.Settings.ExtruderManager import ExtruderManager
@ -35,19 +39,19 @@ class StartJobResult(IntEnum):
ObjectsWithDisabledExtruder = 8
## Formatter class that handles token expansion in start/end gcod
## Formatter class that handles token expansion in start/end gcode
class GcodeStartEndFormatter(Formatter):
def get_value(self, key, args, kwargs): # [CodeStyle: get_value is an overridden function from the Formatter class]
def get_value(self, key: str, *args: str, **kwargs) -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class]
# The kwargs dictionary contains a dictionary for each stack (with a string of the extruder_nr as their key),
# and a default_extruder_nr to use when no extruder_nr is specified
if isinstance(key, str):
try:
extruder_nr = kwargs["default_extruder_nr"]
extruder_nr = int(kwargs["default_extruder_nr"])
except ValueError:
extruder_nr = -1
key_fragments = [fragment.strip() for fragment in key.split(',')]
key_fragments = [fragment.strip() for fragment in key.split(",")]
if len(key_fragments) == 2:
try:
extruder_nr = int(key_fragments[1])
@ -74,25 +78,25 @@ class GcodeStartEndFormatter(Formatter):
## Job class that builds up the message of scene data to send to CuraEngine.
class StartSliceJob(Job):
def __init__(self, slice_message):
def __init__(self, slice_message: Arcus.PythonMessage) -> None:
super().__init__()
self._scene = Application.getInstance().getController().getScene()
self._slice_message = slice_message
self._is_cancelled = False
self._build_plate_number = None
self._scene = CuraApplication.getInstance().getController().getScene() #type: Scene
self._slice_message = slice_message #type: Arcus.PythonMessage
self._is_cancelled = False #type: bool
self._build_plate_number = None #type: Optional[int]
self._all_extruders_settings = None # cache for all setting values from all stacks (global & extruder) for the current machine
self._all_extruders_settings = None #type: Optional[Dict[str, Any]] # cache for all setting values from all stacks (global & extruder) for the current machine
def getSliceMessage(self):
def getSliceMessage(self) -> Arcus.PythonMessage:
return self._slice_message
def setBuildPlate(self, build_plate_number):
def setBuildPlate(self, build_plate_number: int) -> None:
self._build_plate_number = build_plate_number
## Check if a stack has any errors.
## returns true if it has errors, false otherwise.
def _checkStackForErrors(self, stack):
def _checkStackForErrors(self, stack: ContainerStack) -> bool:
if stack is None:
return False
@ -105,28 +109,28 @@ class StartSliceJob(Job):
return False
## Runs the job that initiates the slicing.
def run(self):
def run(self) -> None:
if self._build_plate_number is None:
self.setResult(StartJobResult.Error)
return
stack = Application.getInstance().getGlobalContainerStack()
stack = CuraApplication.getInstance().getGlobalContainerStack()
if not stack:
self.setResult(StartJobResult.Error)
return
# Don't slice if there is a setting with an error value.
if Application.getInstance().getMachineManager().stacksHaveErrors:
if CuraApplication.getInstance().getMachineManager().stacksHaveErrors:
self.setResult(StartJobResult.SettingError)
return
if Application.getInstance().getBuildVolume().hasErrors():
if CuraApplication.getInstance().getBuildVolume().hasErrors():
self.setResult(StartJobResult.BuildPlateError)
return
# Don't slice if the buildplate or the nozzle type is incompatible with the materials
if not Application.getInstance().getMachineManager().variantBuildplateCompatible and \
not Application.getInstance().getMachineManager().variantBuildplateUsable:
if not CuraApplication.getInstance().getMachineManager().variantBuildplateCompatible and \
not CuraApplication.getInstance().getMachineManager().variantBuildplateUsable:
self.setResult(StartJobResult.MaterialIncompatible)
return
@ -141,7 +145,7 @@ class StartSliceJob(Job):
# Don't slice if there is a per object setting with an error value.
for node in DepthFirstIterator(self._scene.getRoot()):
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
if not isinstance(node, CuraSceneNode) or not node.isSelectable():
continue
@ -151,7 +155,7 @@ class StartSliceJob(Job):
with self._scene.getSceneLock():
# Remove old layer data.
for node in DepthFirstIterator(self._scene.getRoot()):
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
if node.callDecoration("getLayerData") and node.callDecoration("getBuildPlateNumber") == self._build_plate_number:
node.getParent().removeChild(node)
break
@ -159,7 +163,7 @@ class StartSliceJob(Job):
# Get the objects in their groups to print.
object_groups = []
if stack.getProperty("print_sequence", "value") == "one_at_a_time":
for node in OneAtATimeIterator(self._scene.getRoot()):
for node in OneAtATimeIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
temp_list = []
# Node can't be printed, so don't bother sending it.
@ -185,7 +189,7 @@ class StartSliceJob(Job):
else:
temp_list = []
has_printing_mesh = False
for node in DepthFirstIterator(self._scene.getRoot()):
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
if node.callDecoration("isSliceable") and node.getMeshData() and node.getMeshData().getVertices() is not None:
per_object_stack = node.callDecoration("getStack")
is_non_printing_mesh = False
@ -212,27 +216,26 @@ class StartSliceJob(Job):
if temp_list:
object_groups.append(temp_list)
extruders_enabled = {position: stack.isEnabled for position, stack in Application.getInstance().getGlobalContainerStack().extruders.items()}
extruders_enabled = {position: stack.isEnabled for position, stack in CuraApplication.getInstance().getGlobalContainerStack().extruders.items()}
filtered_object_groups = []
has_model_with_disabled_extruders = False
associated_siabled_extruders = set()
associated_disabled_extruders = set()
for group in object_groups:
stack = Application.getInstance().getGlobalContainerStack()
stack = CuraApplication.getInstance().getGlobalContainerStack()
skip_group = False
for node in group:
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
associated_disabled_extruders.add(extruder_position)
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))
associated_disabled_extruders = [str(c) for c in sorted([int(p) + 1 for p in associated_disabled_extruders])]
self.setMessage(", ".join(associated_disabled_extruders))
return
# There are cases when there is nothing to slice. This can happen due to one at a time slicing not being
@ -284,11 +287,11 @@ class StartSliceJob(Job):
self.setResult(StartJobResult.Finished)
def cancel(self):
def cancel(self) -> None:
super().cancel()
self._is_cancelled = True
def isCancelled(self):
def isCancelled(self) -> bool:
return self._is_cancelled
## Creates a dictionary of tokens to replace in g-code pieces.
@ -298,7 +301,7 @@ class StartSliceJob(Job):
# with.
# \return A dictionary of replacement tokens to the values they should be
# replaced with.
def _buildReplacementTokens(self, stack) -> dict:
def _buildReplacementTokens(self, stack: ContainerStack) -> Dict[str, Any]:
result = {}
for key in stack.getAllKeys():
value = stack.getProperty(key, "value")
@ -311,7 +314,7 @@ class StartSliceJob(Job):
result["date"] = time.strftime("%d-%m-%Y")
result["day"] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][int(time.strftime("%w"))]
initial_extruder_stack = Application.getInstance().getExtruderManager().getUsedExtruderStacks()[0]
initial_extruder_stack = CuraApplication.getInstance().getExtruderManager().getUsedExtruderStacks()[0]
initial_extruder_nr = initial_extruder_stack.getProperty("extruder_nr", "value")
result["initial_extruder_nr"] = initial_extruder_nr
@ -320,9 +323,9 @@ class StartSliceJob(Job):
## Replace setting tokens in a piece of g-code.
# \param value A piece of g-code to replace tokens in.
# \param default_extruder_nr Stack nr to use when no stack nr is specified, defaults to the global stack
def _expandGcodeTokens(self, value: str, default_extruder_nr: int = -1):
def _expandGcodeTokens(self, value: str, default_extruder_nr: int = -1) -> str:
if not self._all_extruders_settings:
global_stack = Application.getInstance().getGlobalContainerStack()
global_stack = CuraApplication.getInstance().getGlobalContainerStack()
# NB: keys must be strings for the string formatter
self._all_extruders_settings = {
@ -344,7 +347,7 @@ class StartSliceJob(Job):
return str(value)
## Create extruder message from stack
def _buildExtruderMessage(self, stack):
def _buildExtruderMessage(self, stack: ContainerStack) -> None:
message = self._slice_message.addRepeatedMessage("extruders")
message.id = int(stack.getMetaDataEntry("position"))
@ -371,7 +374,7 @@ class StartSliceJob(Job):
#
# The settings are taken from the global stack. This does not include any
# per-extruder settings or per-object settings.
def _buildGlobalSettingsMessage(self, stack):
def _buildGlobalSettingsMessage(self, stack: ContainerStack) -> None:
settings = self._buildReplacementTokens(stack)
# Pre-compute material material_bed_temp_prepend and material_print_temp_prepend
@ -385,7 +388,7 @@ class StartSliceJob(Job):
# Replace the setting tokens in start and end g-code.
# Use values from the first used extruder by default so we get the expected temperatures
initial_extruder_stack = Application.getInstance().getExtruderManager().getUsedExtruderStacks()[0]
initial_extruder_stack = CuraApplication.getInstance().getExtruderManager().getUsedExtruderStacks()[0]
initial_extruder_nr = initial_extruder_stack.getProperty("extruder_nr", "value")
settings["machine_start_gcode"] = self._expandGcodeTokens(settings["machine_start_gcode"], initial_extruder_nr)
@ -406,7 +409,7 @@ class StartSliceJob(Job):
#
# \param stack The global stack with all settings, from which to read the
# limit_to_extruder property.
def _buildGlobalInheritsStackMessage(self, stack):
def _buildGlobalInheritsStackMessage(self, stack: ContainerStack) -> None:
for key in stack.getAllKeys():
extruder_position = int(round(float(stack.getProperty(key, "limit_to_extruder"))))
if extruder_position >= 0: # Set to a specific extruder.
@ -416,9 +419,9 @@ class StartSliceJob(Job):
Job.yieldThread()
## Check if a node has per object settings and ensure that they are set correctly in the message
# \param node \type{SceneNode} Node to check.
# \param node Node to check.
# \param message object_lists message to put the per object settings in
def _handlePerObjectSettings(self, node, message):
def _handlePerObjectSettings(self, node: CuraSceneNode, message: Arcus.PythonMessage):
stack = node.callDecoration("getStack")
# Check if the node has a stack attached to it and the stack has any settings in the top container.
@ -427,7 +430,7 @@ class StartSliceJob(Job):
# Check all settings for relations, so we can also calculate the correct values for dependent settings.
top_of_stack = stack.getTop() # Cache for efficiency.
changed_setting_keys = set(top_of_stack.getAllKeys())
changed_setting_keys = top_of_stack.getAllKeys()
# Add all relations to changed settings as well.
for key in top_of_stack.getAllKeys():
@ -456,9 +459,9 @@ class StartSliceJob(Job):
Job.yieldThread()
## Recursive function to put all settings that require each other for value changes in a list
# \param relations_set \type{set} Set of keys (strings) of settings that are influenced
# \param relations_set Set of keys of settings that are influenced
# \param relations list of relation objects that need to be checked.
def _addRelations(self, relations_set, relations):
def _addRelations(self, relations_set: Set[str], relations: List[SettingRelation]):
for relation in filter(lambda r: r.role == "value" or r.role == "limit_to_extruder", relations):
if relation.type == RelationType.RequiresTarget:
continue

View file

@ -11,9 +11,8 @@ from UM.PluginRegistry import PluginRegistry
#
# If you're zipping g-code, you might as well use gzip!
class GCodeGzReader(MeshReader):
def __init__(self, application):
super().__init__(application)
def __init__(self) -> None:
super().__init__()
self._supported_extensions = [".gcode.gz"]
def _read(self, file_name):

View file

@ -19,6 +19,7 @@ def getMetaData():
]
}
def register(app):
app.addNonSliceableExtension(".gz")
return { "mesh_reader": GCodeGzReader.GCodeGzReader(app) }
return {"mesh_reader": GCodeGzReader.GCodeGzReader()}

View file

@ -3,7 +3,7 @@
import gzip
from io import StringIO, BufferedIOBase #To write the g-code to a temporary buffer, and for typing.
from typing import List
from typing import cast, List
from UM.Logger import Logger
from UM.Mesh.MeshWriter import MeshWriter #The class we're extending/implementing.
@ -32,7 +32,7 @@ class GCodeGzWriter(MeshWriter):
#Get the g-code from the g-code writer.
gcode_textio = StringIO() #We have to convert the g-code into bytes.
success = PluginRegistry.getInstance().getPluginObject("GCodeWriter").write(gcode_textio, None)
success = cast(MeshWriter, PluginRegistry.getInstance().getPluginObject("GCodeWriter")).write(gcode_textio, None)
if not success: #Writing the g-code failed. Then I can also not write the gzipped g-code.
return False

View file

@ -1,11 +1,9 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM.Application import Application
from UM.Backend import Backend
from UM.Job import Job
from UM.Logger import Logger
from UM.Math.AxisAlignedBox import AxisAlignedBox
from UM.Math.Vector import Vector
from UM.Message import Message
from cura.Scene.CuraSceneNode import CuraSceneNode
@ -13,7 +11,8 @@ from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
from cura import LayerDataBuilder
from cura.CuraApplication import CuraApplication
from cura.LayerDataBuilder import LayerDataBuilder
from cura.LayerDataDecorator import LayerDataDecorator
from cura.LayerPolygon import LayerPolygon
from cura.Scene.GCodeListDecorator import GCodeListDecorator
@ -23,16 +22,16 @@ import numpy
import math
import re
from typing import Dict, List, NamedTuple, Optional, Union
from collections import namedtuple
Position = NamedTuple("Position", [("x", float), ("y", float), ("z", float), ("f", float), ("e", float)])
PositionOptional = NamedTuple("Position", [("x", Optional[float]), ("y", Optional[float]), ("z", Optional[float]), ("f", Optional[float]), ("e", Optional[float])])
Position = NamedTuple("Position", [("x", float), ("y", float), ("z", float), ("f", float), ("e", List[float])])
## This parser is intended to interpret the common firmware codes among all the
# different flavors
class FlavorParser:
def __init__(self) -> None:
Application.getInstance().hideMessageSignal.connect(self._onHideMessage)
CuraApplication.getInstance().hideMessageSignal.connect(self._onHideMessage)
self._cancelled = False
self._message = None
self._layer_number = 0
@ -40,21 +39,21 @@ class FlavorParser:
self._clearValues()
self._scene_node = None
# X, Y, Z position, F feedrate and E extruder values are stored
self._position = namedtuple('Position', ['x', 'y', 'z', 'f', 'e'])
self._position = Position
self._is_layers_in_file = False # Does the Gcode have the layers comment?
self._extruder_offsets = {} # Offsets for multi extruders. key is index, value is [x-offset, y-offset]
self._extruder_offsets = {} # type: Dict[int, List[float]] # Offsets for multi extruders. key is index, value is [x-offset, y-offset]
self._current_layer_thickness = 0.2 # default
self._filament_diameter = 2.85 # default
Application.getInstance().getPreferences().addPreference("gcodereader/show_caution", True)
CuraApplication.getInstance().getPreferences().addPreference("gcodereader/show_caution", True)
def _clearValues(self) -> None:
self._extruder_number = 0
self._extrusion_length_offset = [0]
self._extrusion_length_offset = [0] # type: List[float]
self._layer_type = LayerPolygon.Inset0Type
self._layer_number = 0
self._previous_z = 0
self._layer_data_builder = LayerDataBuilder.LayerDataBuilder()
self._previous_z = 0 # type: float
self._layer_data_builder = LayerDataBuilder()
self._is_absolute_positioning = True # It can be absolute (G90) or relative (G91)
self._is_absolute_extrusion = True # It can become absolute (M82, default) or relative (M83)
@ -77,14 +76,14 @@ class FlavorParser:
def _getInt(self, line: str, code: str) -> Optional[int]:
value = self._getValue(line, code)
try:
return int(value)
return int(value) # type: ignore
except:
return None
def _getFloat(self, line: str, code: str) -> Optional[float]:
value = self._getValue(line, code)
try:
return float(value)
return float(value) # type: ignore
except:
return None
@ -92,10 +91,6 @@ class FlavorParser:
if message == self._message:
self._cancelled = True
@staticmethod
def _getNullBoundingBox() -> AxisAlignedBox:
return AxisAlignedBox(minimum=Vector(0, 0, 0), maximum=Vector(10, 10, 10))
def _createPolygon(self, layer_thickness: float, path: List[List[Union[float, int]]], extruder_offsets: List[float]) -> bool:
countvalid = 0
for point in path:
@ -169,7 +164,7 @@ class FlavorParser:
return 0.35
return line_width
def _gCode0(self, position: Position, params: Position, path: List[List[Union[float, int]]]) -> Position:
def _gCode0(self, position: Position, params: PositionOptional, path: List[List[Union[float, int]]]) -> Position:
x, y, z, f, e = position
if self._is_absolute_positioning:
@ -205,7 +200,7 @@ class FlavorParser:
_gCode1 = _gCode0
## Home the head.
def _gCode28(self, position: Position, params: Position, path: List[List[Union[float, int]]]) -> Position:
def _gCode28(self, position: Position, params: PositionOptional, path: List[List[Union[float, int]]]) -> Position:
return self._position(
params.x if params.x is not None else position.x,
params.y if params.y is not None else position.y,
@ -214,20 +209,20 @@ class FlavorParser:
position.e)
## Set the absolute positioning
def _gCode90(self, position: Position, params: Position, path: List[List[Union[float, int]]]) -> Position:
def _gCode90(self, position: Position, params: PositionOptional, path: List[List[Union[float, int]]]) -> Position:
self._is_absolute_positioning = True
self._is_absolute_extrusion = True
return position
## Set the relative positioning
def _gCode91(self, position: Position, params: Position, path: List[List[Union[float, int]]]) -> Position:
def _gCode91(self, position: Position, params: PositionOptional, path: List[List[Union[float, int]]]) -> Position:
self._is_absolute_positioning = False
self._is_absolute_extrusion = False
return position
## Reset the current position to the values specified.
# For example: G92 X10 will set the X to 10 without any physical motion.
def _gCode92(self, position: Position, params: Position, path: List[List[Union[float, int]]]) -> Position:
def _gCode92(self, position: Position, params: PositionOptional, path: List[List[Union[float, int]]]) -> Position:
if params.e is not None:
# Sometimes a G92 E0 is introduced in the middle of the GCode so we need to keep those offsets for calculate the line_width
self._extrusion_length_offset[self._extruder_number] += position.e[self._extruder_number] - params.e
@ -260,7 +255,7 @@ class FlavorParser:
f = float(item[1:]) / 60
if item[0] == "E":
e = float(item[1:])
params = self._position(x, y, z, f, e)
params = PositionOptional(x, y, z, f, e)
return func(position, params, path)
return position
@ -290,13 +285,10 @@ class FlavorParser:
Logger.log("d", "Preparing to load GCode")
self._cancelled = False
# We obtain the filament diameter from the selected extruder to calculate line widths
global_stack = Application.getInstance().getGlobalContainerStack()
global_stack = CuraApplication.getInstance().getGlobalContainerStack()
self._filament_diameter = global_stack.extruders[str(self._extruder_number)].getProperty("material_diameter", "value")
scene_node = CuraSceneNode()
# Override getBoundingBox function of the sceneNode, as this node should return a bounding box, but there is no
# real data to calculate it from.
scene_node.getBoundingBox = self._getNullBoundingBox
gcode_list = []
self._is_layers_in_file = False
@ -322,13 +314,14 @@ class FlavorParser:
lifetime=0,
title = catalog.i18nc("@info:title", "G-code Details"))
assert(self._message is not None) # use for typing purposes
self._message.setProgress(0)
self._message.show()
Logger.log("d", "Parsing Gcode...")
current_position = self._position(0, 0, 0, 0, [0])
current_path = []
current_position = Position(0, 0, 0, 0, [0])
current_path = [] #type: List[List[float]]
min_layer_number = 0
negative_layers = 0
previous_layer = 0
@ -443,9 +436,9 @@ class FlavorParser:
scene_node.addDecorator(gcode_list_decorator)
# gcode_dict stores gcode_lists for a number of build plates.
active_build_plate_id = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
active_build_plate_id = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
gcode_dict = {active_build_plate_id: gcode_list}
Application.getInstance().getController().getScene().gcode_dict = gcode_dict
CuraApplication.getInstance().getController().getScene().gcode_dict = gcode_dict #type: ignore #Because gcode_dict is generated dynamically.
Logger.log("d", "Finished parsing Gcode")
self._message.hide()
@ -453,7 +446,7 @@ class FlavorParser:
if self._layer_number == 0:
Logger.log("w", "File doesn't contain any valid layers")
settings = Application.getInstance().getGlobalContainerStack()
settings = CuraApplication.getInstance().getGlobalContainerStack()
if not settings.getProperty("machine_center_is_zero", "value"):
machine_width = settings.getProperty("machine_width", "value")
machine_depth = settings.getProperty("machine_depth", "value")
@ -461,7 +454,7 @@ class FlavorParser:
Logger.log("d", "GCode loading finished")
if Application.getInstance().getPreferences().getValue("gcodereader/show_caution"):
if CuraApplication.getInstance().getPreferences().getValue("gcodereader/show_caution"):
caution_message = Message(catalog.i18nc(
"@info:generic",
"Make sure the g-code is suitable for your printer and printer configuration before sending the file to it. The g-code representation may not be accurate."),
@ -470,7 +463,7 @@ class FlavorParser:
caution_message.show()
# The "save/print" button's state is bound to the backend state.
backend = Application.getInstance().getBackend()
backend = CuraApplication.getInstance().getBackend()
backend.backendStateChange.emit(Backend.BackendState.Disabled)
return scene_node

View file

@ -19,16 +19,16 @@ MimeTypeDatabase.addMimeType(
)
)
# Class for loading and parsing G-code files
class GCodeReader(MeshReader):
_flavor_default = "Marlin"
_flavor_keyword = ";FLAVOR:"
_flavor_readers_dict = {"RepRap" : RepRapFlavorParser.RepRapFlavorParser(),
"Marlin" : MarlinFlavorParser.MarlinFlavorParser()}
def __init__(self, application):
super(GCodeReader, self).__init__(application)
def __init__(self) -> None:
super().__init__()
self._supported_extensions = [".gcode", ".g"]
self._flavor_reader = None

View file

@ -20,7 +20,8 @@ def getMetaData():
]
}
def register(app):
app.addNonSliceableExtension(".gcode")
app.addNonSliceableExtension(".g")
return { "mesh_reader": GCodeReader.GCodeReader(app) }
return {"mesh_reader": GCodeReader.GCodeReader()}

View file

@ -140,7 +140,7 @@ class GCodeWriter(MeshWriter):
serialized = flat_global_container.serialize()
data = {"global_quality": serialized}
all_setting_keys = set(flat_global_container.getAllKeys())
all_setting_keys = flat_global_container.getAllKeys()
for extruder in sorted(stack.extruders.values(), key = lambda k: int(k.getMetaDataEntry("position"))):
extruder_quality = extruder.qualityChanges
if extruder_quality.getId() == "empty_quality_changes":
@ -167,7 +167,7 @@ class GCodeWriter(MeshWriter):
extruder_serialized = flat_extruder_quality.serialize()
data.setdefault("extruder_quality", []).append(extruder_serialized)
all_setting_keys.update(set(flat_extruder_quality.getAllKeys()))
all_setting_keys.update(flat_extruder_quality.getAllKeys())
# Check if there is any profiles
if not all_setting_keys:

View file

@ -17,8 +17,8 @@ from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode
class ImageReader(MeshReader):
def __init__(self, application):
super(ImageReader, self).__init__(application)
def __init__(self) -> None:
super().__init__()
self._supported_extensions = [".jpg", ".jpeg", ".bmp", ".gif", ".png"]
self._ui = ImageReaderUI(self)

View file

@ -32,5 +32,6 @@ def getMetaData():
]
}
def register(app):
return { "mesh_reader": ImageReader.ImageReader(app) }
return {"mesh_reader": ImageReader.ImageReader()}

View file

@ -1,9 +1,14 @@
# This PostProcessing Plugin script is released
# under the terms of the AGPLv3 or higher
from typing import Optional, Tuple
from UM.Logger import Logger
from ..Script import Script
class FilamentChange(Script):
_layer_keyword = ";LAYER:"
def __init__(self):
super().__init__()
@ -64,11 +69,26 @@ class FilamentChange(Script):
if len(layer_targets) > 0:
for layer_num in layer_targets:
layer_num = int(layer_num.strip())
if layer_num < len(data):
layer = data[layer_num - 1]
lines = layer.split("\n")
if layer_num <= len(data):
index, layer_data = self._searchLayerData(data, layer_num - 1)
if layer_data is None:
Logger.log("e", "Could not found the layer")
continue
lines = layer_data.split("\n")
lines.insert(2, color_change)
final_line = "\n".join(lines)
data[layer_num - 1] = final_line
data[index] = final_line
return data
## This method returns the data corresponding with the indicated layer number, looking in the gcode for
# the occurrence of this layer number.
def _searchLayerData(self, data: list, layer_num: int) -> Tuple[int, Optional[str]]:
for index, layer_data in enumerate(data):
first_line = layer_data.split("\n")[0]
# The first line should contain the layer number at the beginning.
if first_line[:len(self._layer_keyword)] == self._layer_keyword:
# If found the layer that we are looking for, then return the data
if first_line[len(self._layer_keyword):] == str(layer_num):
return index, layer_data
return 0, None

View file

@ -105,14 +105,6 @@ class PauseAtHeight(Script):
"unit": "°C",
"type": "int",
"default_value": 0
},
"resume_temperature":
{
"label": "Resume Temperature",
"description": "Change the temperature after the pause",
"unit": "°C",
"type": "int",
"default_value": 0
}
}
}"""
@ -144,7 +136,6 @@ class PauseAtHeight(Script):
layers_started = False
redo_layers = self.getSettingValueByKey("redo_layers")
standby_temperature = self.getSettingValueByKey("standby_temperature")
resume_temperature = self.getSettingValueByKey("resume_temperature")
# T = ExtruderManager.getInstance().getActiveExtruderStack().getProperty("material_print_temperature", "value")
@ -152,6 +143,8 @@ class PauseAtHeight(Script):
layer_0_z = 0.
current_z = 0
got_first_g_cmd_on_layer_0 = False
current_t = 0 #Tracks the current extruder for tracking the target temperature.
target_temperature = {} #Tracks the current target temperature for each extruder.
nbr_negative_layers = 0
@ -169,6 +162,16 @@ class PauseAtHeight(Script):
if not layers_started:
continue
#Track the latest printing temperature in order to resume at the correct temperature.
if line.startswith("T"):
current_t = self.getValue(line, "T")
m = self.getValue(line, "M")
if m is not None and (m == 104 or m == 109) and self.getValue(line, "S") is not None:
extruder = current_t
if self.getValue(line, "T") is not None:
extruder = self.getValue(line, "T")
target_temperature[extruder] = self.getValue(line, "S")
# If a Z instruction is in the line, read the current Z
if self.getValue(line, "Z") is not None:
current_z = self.getValue(line, "Z")
@ -262,9 +265,6 @@ class PauseAtHeight(Script):
if current_z < 15:
prepend_gcode += self.putValue(G=1, Z=15, F=300) + "\n"
# Disable the E steppers
prepend_gcode += self.putValue(M=84, E=0) + "\n"
# Set extruder standby temperature
prepend_gcode += self.putValue(M=104, S=standby_temperature) + "; standby temperature\n"
@ -272,7 +272,7 @@ class PauseAtHeight(Script):
prepend_gcode += self.putValue(M=0) + ";Do the actual pause\n"
# Set extruder resume temperature
prepend_gcode += self.putValue(M=109, S=resume_temperature) + "; resume temperature\n"
prepend_gcode += self.putValue(M = 109, S = int(target_temperature.get(current_t, default = 0))) + "; resume temperature\n"
# Push the filament back,
if retraction_amount != 0:

View file

@ -1,22 +1,17 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os
import os.path
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtWidgets import QApplication
from UM.Math.Vector import Vector
from UM.Tool import Tool
from UM.Application import Application
from UM.Event import Event, MouseEvent
from UM.Mesh.MeshBuilder import MeshBuilder
from UM.Scene.Selection import Selection
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from cura.Scene.CuraSceneNode import CuraSceneNode
from cura.CuraApplication import CuraApplication
from cura.Scene.CuraSceneNode import CuraSceneNode
from cura.PickingPass import PickingPass
from UM.Operations.GroupedOperation import GroupedOperation
@ -26,8 +21,6 @@ from cura.Operations.SetParentOperation import SetParentOperation
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
from UM.Scene.GroupDecorator import GroupDecorator
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
from UM.Settings.SettingInstance import SettingInstance
@ -38,7 +31,7 @@ class SupportEraser(Tool):
self._controller = self.getController()
self._selection_pass = None
Application.getInstance().globalContainerStackChanged.connect(self._updateEnabled)
CuraApplication.getInstance().globalContainerStackChanged.connect(self._updateEnabled)
# Note: if the selection is cleared with this tool active, there is no way to switch to
# another tool than to reselect an object (by clicking it) because the tool buttons in the
@ -106,7 +99,7 @@ class SupportEraser(Tool):
mesh.addCube(10,10,10)
node.setMeshData(mesh.build())
active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
node.addDecorator(BuildPlateDecorator(active_build_plate))
node.addDecorator(SliceableObjectDecorator())
@ -126,7 +119,7 @@ class SupportEraser(Tool):
op.push()
node.setPosition(position, CuraSceneNode.TransformSpace.World)
Application.getInstance().getController().getScene().sceneChanged.emit(node)
CuraApplication.getInstance().getController().getScene().sceneChanged.emit(node)
def _removeEraserMesh(self, node: CuraSceneNode):
parent = node.getParent()
@ -139,16 +132,16 @@ class SupportEraser(Tool):
if parent and not Selection.isSelected(parent):
Selection.add(parent)
Application.getInstance().getController().getScene().sceneChanged.emit(node)
CuraApplication.getInstance().getController().getScene().sceneChanged.emit(node)
def _updateEnabled(self):
plugin_enabled = False
global_container_stack = Application.getInstance().getGlobalContainerStack()
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
if global_container_stack:
plugin_enabled = global_container_stack.getProperty("anti_overhang_mesh", "enabled")
Application.getInstance().getController().toolEnabledChanged.emit(self._plugin_id, plugin_enabled)
CuraApplication.getInstance().getController().toolEnabledChanged.emit(self._plugin_id, plugin_enabled)
def _onSelectionChanged(self):
# When selection is passed from one object to another object, first the selection is cleared

View file

@ -9,4 +9,4 @@ def getMetaData():
def register(app):
return {"extension": Toolbox.Toolbox()}
return {"extension": Toolbox.Toolbox(app)}

View file

@ -92,6 +92,12 @@ Item
font: UM.Theme.getFont("very_small")
color: UM.Theme.getColor("text_medium")
}
Label
{
text: catalog.i18nc("@label", "Downloads") + ":"
font: UM.Theme.getFont("very_small")
color: UM.Theme.getColor("text_medium")
}
}
Column
{
@ -138,6 +144,12 @@ Item
linkColor: UM.Theme.getColor("text_link")
onLinkActivated: Qt.openUrlExternally(link)
}
Label
{
text: details.download_count || catalog.i18nc("@label", "Unknown")
font: UM.Theme.getFont("very_small")
color: UM.Theme.getColor("text")
}
}
Rectangle
{

View file

@ -12,7 +12,7 @@ Column
height: childrenRect.height
width: parent.width
spacing: UM.Theme.getSize("default_margin").height
/* Hidden for 3.4
Label
{
id: heading
@ -21,7 +21,6 @@ Column
color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("medium")
}
*/
GridLayout
{
id: grid

View file

@ -32,6 +32,7 @@ class PackagesModel(ListModel):
self.addRoleName(Qt.UserRole + 15, "is_installed") # Scheduled pkgs are included in the model but should not be marked as actually installed
self.addRoleName(Qt.UserRole + 16, "has_configs")
self.addRoleName(Qt.UserRole + 17, "supported_configs")
self.addRoleName(Qt.UserRole + 18, "download_count")
# List of filters for queries. The result is the union of the each list of results.
self._filter = {} # type: Dict[str, str]
@ -76,7 +77,9 @@ class PackagesModel(ListModel):
"is_enabled": package["is_enabled"] if "is_enabled" in package else False,
"is_installed": package["is_installed"] if "is_installed" in package else False,
"has_configs": has_configs,
"supported_configs": configs_model
"supported_configs": configs_model,
"download_count": package["download_count"] if "download_count" in package else 0
})
# Filter on all the key-word arguments.

View file

@ -1,19 +1,20 @@
# Copyright (c) 2018 Ultimaker B.V.
# Toolbox is released under the terms of the LGPLv3 or higher.
from typing import Dict, Optional, Union, Any
from typing import Dict, Optional, Union, Any, cast
import json
import os
import tempfile
import platform
from typing import List
from PyQt5.QtCore import QUrl, QObject, pyqtProperty, pyqtSignal, pyqtSlot
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
from UM.Application import Application
from UM.Logger import Logger
from UM.PluginRegistry import PluginRegistry
from UM.Extension import Extension
from UM.Qt.ListModel import ListModel
from UM.i18n import i18nCatalog
from UM.Version import Version
@ -27,44 +28,39 @@ i18n_catalog = i18nCatalog("cura")
## The Toolbox class is responsible of communicating with the server through the API
class Toolbox(QObject, Extension):
DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" #type: str
DEFAULT_CLOUD_API_VERSION = 1 #type: int
DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com"
DEFAULT_CLOUD_API_VERSION = 1
def __init__(self, application: CuraApplication) -> None:
super().__init__()
def __init__(self, parent=None) -> None:
super().__init__(parent)
self._application = application #type: CuraApplication
self._application = Application.getInstance()
self._package_manager = None
self._plugin_registry = Application.getInstance().getPluginRegistry()
self._sdk_version = None
self._cloud_api_version = None
self._cloud_api_root = None
self._api_url = None
self._sdk_version = None # type: Optional[int]
self._cloud_api_version = None # type: Optional[int]
self._cloud_api_root = None # type: Optional[str]
self._api_url = None # type: Optional[str]
# Network:
self._get_packages_request = None
self._get_showcase_request = None
self._download_request = None
self._download_reply = None
self._download_progress = 0
self._is_downloading = False
self._network_manager = None
self._download_request = None #type: Optional[QNetworkRequest]
self._download_reply = None #type: Optional[QNetworkReply]
self._download_progress = 0 #type: float
self._is_downloading = False #type: bool
self._network_manager = None #type: Optional[QNetworkAccessManager]
self._request_header = [
b"User-Agent",
str.encode(
"%s/%s (%s %s)" % (
Application.getInstance().getApplicationName(),
Application.getInstance().getVersion(),
self._application.getApplicationName(),
self._application.getVersion(),
platform.system(),
platform.machine(),
)
)
]
self._request_urls = {}
self._to_update = [] # Package_ids that are waiting to be updated
self._old_plugin_ids = []
self._request_urls = {} # type: Dict[str, QUrl]
self._to_update = [] # type: List[str] # Package_ids that are waiting to be updated
self._old_plugin_ids = [] # type: List[str]
# Data:
self._metadata = {
@ -76,7 +72,7 @@ class Toolbox(QObject, Extension):
"materials_showcase": [],
"materials_available": [],
"materials_installed": []
}
} # type: Dict[str, List[Any]]
# Models:
self._models = {
@ -88,33 +84,33 @@ class Toolbox(QObject, Extension):
"materials_showcase": AuthorsModel(self),
"materials_available": PackagesModel(self),
"materials_installed": PackagesModel(self)
}
} # type: Dict[str, ListModel]
# These properties are for keeping track of the UI state:
# ----------------------------------------------------------------------
# View category defines which filter to use, and therefore effectively
# which category is currently being displayed. For example, possible
# values include "plugin" or "material", but also "installed".
self._view_category = "plugin"
self._view_category = "plugin" #type: str
# View page defines which type of page layout to use. For example,
# possible values include "overview", "detail" or "author".
self._view_page = "loading"
self._view_page = "loading" #type: str
# Active package refers to which package is currently being downloaded,
# installed, or otherwise modified.
self._active_package = None
self._active_package = None # type: Optional[Dict[str, Any]]
self._dialog = None
self._restart_required = False
self._dialog = None #type: Optional[QObject]
self._restart_required = False #type: bool
# variables for the license agreement dialog
self._license_dialog_plugin_name = ""
self._license_dialog_license_content = ""
self._license_dialog_plugin_file_location = ""
self._restart_dialog_message = ""
self._license_dialog_plugin_name = "" #type: str
self._license_dialog_license_content = "" #type: str
self._license_dialog_plugin_file_location = "" #type: str
self._restart_dialog_message = "" #type: str
Application.getInstance().initializationFinished.connect(self._onAppInitialized)
self._application.initializationFinished.connect(self._onAppInitialized)
@ -156,7 +152,8 @@ class Toolbox(QObject, Extension):
# This is a plugin, so most of the components required are not ready when
# this is initialized. Therefore, we wait until the application is ready.
def _onAppInitialized(self) -> None:
self._package_manager = Application.getInstance().getPackageManager()
self._plugin_registry = self._application.getPluginRegistry()
self._package_manager = self._application.getPackageManager()
self._sdk_version = self._getSDKVersion()
self._cloud_api_version = self._getCloudAPIVersion()
self._cloud_api_root = self._getCloudAPIRoot()
@ -178,38 +175,38 @@ class Toolbox(QObject, Extension):
def _getCloudAPIRoot(self) -> str:
if not hasattr(cura, "CuraVersion"):
return self.DEFAULT_CLOUD_API_ROOT
if not hasattr(cura.CuraVersion, "CuraCloudAPIRoot"):
if not hasattr(cura.CuraVersion, "CuraCloudAPIRoot"): # type: ignore
return self.DEFAULT_CLOUD_API_ROOT
if not cura.CuraVersion.CuraCloudAPIRoot:
if not cura.CuraVersion.CuraCloudAPIRoot: # type: ignore
return self.DEFAULT_CLOUD_API_ROOT
return cura.CuraVersion.CuraCloudAPIRoot
return cura.CuraVersion.CuraCloudAPIRoot # type: ignore
# Get the cloud API version from CuraVersion
def _getCloudAPIVersion(self) -> int:
if not hasattr(cura, "CuraVersion"):
return self.DEFAULT_CLOUD_API_VERSION
if not hasattr(cura.CuraVersion, "CuraCloudAPIVersion"):
if not hasattr(cura.CuraVersion, "CuraCloudAPIVersion"): # type: ignore
return self.DEFAULT_CLOUD_API_VERSION
if not cura.CuraVersion.CuraCloudAPIVersion:
if not cura.CuraVersion.CuraCloudAPIVersion: # type: ignore
return self.DEFAULT_CLOUD_API_VERSION
return cura.CuraVersion.CuraCloudAPIVersion
return cura.CuraVersion.CuraCloudAPIVersion # type: ignore
# Get the packages version depending on Cura version settings.
def _getSDKVersion(self) -> int:
if not hasattr(cura, "CuraVersion"):
return self._plugin_registry.APIVersion
if not hasattr(cura.CuraVersion, "CuraSDKVersion"):
if not hasattr(cura.CuraVersion, "CuraSDKVersion"): # type: ignore
return self._plugin_registry.APIVersion
if not cura.CuraVersion.CuraSDKVersion:
if not cura.CuraVersion.CuraSDKVersion: # type: ignore
return self._plugin_registry.APIVersion
return cura.CuraVersion.CuraSDKVersion
return cura.CuraVersion.CuraSDKVersion # type: ignore
@pyqtSlot()
def browsePackages(self) -> None:
# Create the network manager:
# This was formerly its own function but really had no reason to be as
# it was never called more than once ever.
if self._network_manager:
if self._network_manager is not None:
self._network_manager.finished.disconnect(self._onRequestFinished)
self._network_manager.networkAccessibleChanged.disconnect(self._onNetworkAccessibleChanged)
self._network_manager = QNetworkAccessManager()
@ -235,11 +232,11 @@ class Toolbox(QObject, Extension):
def _createDialog(self, qml_name: str) -> Optional[QObject]:
Logger.log("d", "Toolbox: Creating dialog [%s].", qml_name)
path = os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "resources", "qml", qml_name)
dialog = Application.getInstance().createQmlComponent(path, {"toolbox": self})
dialog = self._application.createQmlComponent(path, {"toolbox": self})
return dialog
def _convertPluginMetadata(self, plugin: dict) -> dict:
def _convertPluginMetadata(self, plugin: Dict[str, Any]) -> Dict[str, Any]:
formatted = {
"package_id": plugin["id"],
"package_type": "plugin",
@ -257,7 +254,6 @@ class Toolbox(QObject, Extension):
@pyqtSlot()
def _updateInstalledModels(self) -> None:
# This is moved here to avoid code duplication and so that after installing plugins they get removed from the
# list of old plugins
old_plugin_ids = self._plugin_registry.getInstalledPlugins()
@ -265,7 +261,7 @@ class Toolbox(QObject, Extension):
scheduled_to_remove_package_ids = self._package_manager.getToRemovePackageIDs()
self._old_plugin_ids = []
self._old_plugin_metadata = []
self._old_plugin_metadata = [] # type: List[Dict[str, Any]]
for plugin_id in old_plugin_ids:
# Neither the installed packages nor the packages that are scheduled to remove are old plugins
@ -353,8 +349,8 @@ class Toolbox(QObject, Extension):
return self._restart_required
@pyqtSlot()
def restart(self):
CuraApplication.getInstance().windowClosed()
def restart(self) -> None:
self._application.windowClosed()
def getRemotePackage(self, package_id: str) -> Optional[Dict]:
# TODO: make the lookup in a dict, not a loop. canUpdate is called for every item.
@ -432,7 +428,8 @@ class Toolbox(QObject, Extension):
Logger.log("i", "Toolbox: Requesting %s metadata from server.", type)
request = QNetworkRequest(self._request_urls[type])
request.setRawHeader(*self._request_header)
self._network_manager.get(request)
if self._network_manager:
self._network_manager.get(request)
@pyqtSlot(str)
def startDownload(self, url: str) -> None:
@ -441,15 +438,15 @@ class Toolbox(QObject, Extension):
self._download_request = QNetworkRequest(url)
if hasattr(QNetworkRequest, "FollowRedirectsAttribute"):
# Patch for Qt 5.6-5.8
self._download_request.setAttribute(QNetworkRequest.FollowRedirectsAttribute, True)
cast(QNetworkRequest, self._download_request).setAttribute(QNetworkRequest.FollowRedirectsAttribute, True)
if hasattr(QNetworkRequest, "RedirectPolicyAttribute"):
# Patch for Qt 5.9+
self._download_request.setAttribute(QNetworkRequest.RedirectPolicyAttribute, True)
self._download_request.setRawHeader(*self._request_header)
self._download_reply = self._network_manager.get(self._download_request)
cast(QNetworkRequest, self._download_request).setAttribute(QNetworkRequest.RedirectPolicyAttribute, True)
cast(QNetworkRequest, self._download_request).setRawHeader(*self._request_header)
self._download_reply = cast(QNetworkAccessManager, self._network_manager).get(self._download_request)
self.setDownloadProgress(0)
self.setIsDownloading(True)
self._download_reply.downloadProgress.connect(self._onDownloadProgress)
cast(QNetworkReply, self._download_reply).downloadProgress.connect(self._onDownloadProgress)
@pyqtSlot()
def cancelDownload(self) -> None:
@ -475,7 +472,6 @@ class Toolbox(QObject, Extension):
self.resetDownload()
def _onRequestFinished(self, reply: QNetworkReply) -> None:
if reply.error() == QNetworkReply.TimeoutError:
Logger.log("w", "Got a timeout.")
self.setViewPage("errored")
@ -551,12 +547,12 @@ class Toolbox(QObject, Extension):
self.setDownloadProgress(new_progress)
if bytes_sent == bytes_total:
self.setIsDownloading(False)
self._download_reply.downloadProgress.disconnect(self._onDownloadProgress)
cast(QNetworkReply, self._download_reply).downloadProgress.disconnect(self._onDownloadProgress)
# Must not delete the temporary file on Windows
self._temp_plugin_file = tempfile.NamedTemporaryFile(mode = "w+b", suffix = ".curapackage", delete = False)
file_path = self._temp_plugin_file.name
# Write first and close, otherwise on Windows, it cannot read the file
self._temp_plugin_file.write(self._download_reply.readAll())
self._temp_plugin_file.write(cast(QNetworkReply, self._download_reply).readAll())
self._temp_plugin_file.close()
self._onDownloadComplete(file_path)
@ -577,13 +573,13 @@ class Toolbox(QObject, Extension):
# Getter & Setters for Properties:
# --------------------------------------------------------------------------
def setDownloadProgress(self, progress: Union[int, float]) -> None:
def setDownloadProgress(self, progress: float) -> None:
if progress != self._download_progress:
self._download_progress = progress
self.onDownloadProgressChanged.emit()
@pyqtProperty(int, fset = setDownloadProgress, notify = onDownloadProgressChanged)
def downloadProgress(self) -> int:
def downloadProgress(self) -> float:
return self._download_progress
def setIsDownloading(self, is_downloading: bool) -> None:

View file

@ -1,10 +1,12 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Any, cast, Set, Tuple, Union
from UM.FileHandler.FileHandler import FileHandler
from UM.FileHandler.FileWriter import FileWriter #To choose based on the output file mode (text vs. binary).
from UM.FileHandler.WriteFileJob import WriteFileJob #To call the file writer asynchronously.
from UM.Logger import Logger
from UM.Application import Application
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.i18n import i18nCatalog
from UM.Message import Message
@ -13,6 +15,7 @@ from UM.OutputDevice import OutputDeviceError #To show that something went wrong
from UM.Scene.SceneNode import SceneNode #For typing.
from UM.Version import Version #To check against firmware versions for support.
from cura.CuraApplication import CuraApplication
from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
@ -45,15 +48,15 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
# Inheritance doesn't seem to work. Tying them together does work, but i'm open for better suggestions.
clusterPrintersChanged = pyqtSignal()
def __init__(self, device_id, address, properties, parent = None):
def __init__(self, device_id, address, properties, parent = None) -> None:
super().__init__(device_id = device_id, address = address, properties=properties, parent = parent)
self._api_prefix = "/cluster-api/v1/"
self._number_of_extruders = 2
self._dummy_lambdas = set()
self._dummy_lambdas = ("", {}, io.BytesIO()) #type: Tuple[str, Dict, Union[io.StringIO, io.BytesIO]]
self._print_jobs = []
self._print_jobs = [] # type: List[PrintJobOutputModel]
self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ClusterMonitorItem.qml")
self._control_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ClusterControlItem.qml")
@ -61,18 +64,18 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
# See comments about this hack with the clusterPrintersChanged signal
self.printersChanged.connect(self.clusterPrintersChanged)
self._accepts_commands = True
self._accepts_commands = True #type: bool
# Cluster does not have authentication, so default to authenticated
self._authentication_state = AuthState.Authenticated
self._error_message = None
self._write_job_progress_message = None
self._progress_message = None
self._error_message = None #type: Optional[Message]
self._write_job_progress_message = None #type: Optional[Message]
self._progress_message = None #type: Optional[Message]
self._active_printer = None # type: Optional[PrinterOutputModel]
self._printer_selection_dialog = None
self._printer_selection_dialog = None #type: QObject
self.setPriority(3) # Make sure the output device gets selected above local file output
self.setName(self._id)
@ -81,15 +84,15 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self.setConnectionText(i18n_catalog.i18nc("@info:status", "Connected over the network"))
self._printer_uuid_to_unique_name_mapping = {}
self._printer_uuid_to_unique_name_mapping = {} # type: Dict[str, str]
self._finished_jobs = []
self._finished_jobs = [] # type: List[PrintJobOutputModel]
self._cluster_size = int(properties.get(b"cluster_size", 0))
self._latest_reply_handler = None
self._latest_reply_handler = None #type: Optional[QNetworkReply]
def requestWrite(self, nodes: List[SceneNode], file_name=None, filter_by_machine=False, file_handler=None, **kwargs):
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
self.writeStarted.emit(self)
self.sendMaterialProfiles()
@ -98,10 +101,10 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
if file_handler:
file_formats = file_handler.getSupportedFileTypesWrite()
else:
file_formats = Application.getInstance().getMeshFileHandler().getSupportedFileTypesWrite()
file_formats = CuraApplication.getInstance().getMeshFileHandler().getSupportedFileTypesWrite()
#Create a list from the supported file formats string.
machine_file_formats = Application.getInstance().getGlobalContainerStack().getMetaDataEntry("file_formats").split(";")
machine_file_formats = CuraApplication.getInstance().getGlobalContainerStack().getMetaDataEntry("file_formats").split(";")
machine_file_formats = [file_type.strip() for file_type in machine_file_formats]
#Exception for UM3 firmware version >=4.4: UFP is now supported and should be the preferred file format.
if "application/x-ufp" not in machine_file_formats and self.printerType == "ultimaker3" and Version(self.firmwareVersion) >= Version("4.4"):
@ -118,9 +121,9 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
#Just take the first file format available.
if file_handler is not None:
writer = file_handler.getWriterByMimeType(preferred_format["mime_type"])
writer = file_handler.getWriterByMimeType(cast(str, preferred_format["mime_type"]))
else:
writer = Application.getInstance().getMeshFileHandler().getWriterByMimeType(preferred_format["mime_type"])
writer = CuraApplication.getInstance().getMeshFileHandler().getWriterByMimeType(cast(str, preferred_format["mime_type"]))
#This function pauses with the yield, waiting on instructions on which printer it needs to print with.
self._sending_job = self._sendPrintJob(writer, preferred_format, nodes)
@ -136,7 +139,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
def _spawnPrinterSelectionDialog(self):
if self._printer_selection_dialog is None:
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "PrintWindow.qml")
self._printer_selection_dialog = Application.getInstance().createQmlComponent(path, {"OutputDevice": self})
self._printer_selection_dialog = CuraApplication.getInstance().createQmlComponent(path, {"OutputDevice": self})
if self._printer_selection_dialog is not None:
self._printer_selection_dialog.show()
@ -182,10 +185,10 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
target_printer = yield #Potentially wait on the user to select a target printer.
# Using buffering greatly reduces the write time for many lines of gcode
stream = io.BytesIO() # type: Union[io.BytesIO, io.StringIO]# Binary mode.
if preferred_format["mode"] == FileWriter.OutputMode.TextMode:
stream = io.StringIO()
else: #Binary mode.
stream = io.BytesIO()
job = WriteFileJob(writer, stream, nodes, preferred_format["mode"])
@ -201,7 +204,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
yield True #Return that we had success!
yield #To prevent having to catch the StopIteration exception.
def _sendPrintJobWaitOnWriteJobFinished(self, job):
def _sendPrintJobWaitOnWriteJobFinished(self, job: WriteFileJob) -> None:
self._write_job_progress_message.hide()
self._progress_message = Message(i18n_catalog.i18nc("@info:status", "Sending data to printer"), lifetime = 0, dismissable = False, progress = -1,
@ -222,40 +225,41 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
# Add user name to the print_job
parts.append(self.createFormPart("name=owner", bytes(self._getUserName(), "utf-8"), "text/plain"))
file_name = Application.getInstance().getPrintInformation().jobName + "." + preferred_format["extension"]
file_name = CuraApplication.getInstance().getPrintInformation().jobName + "." + preferred_format["extension"]
output = stream.getvalue() #Either str or bytes depending on the output mode.
if isinstance(stream, io.StringIO):
output = output.encode("utf-8")
output = cast(str, output).encode("utf-8")
output = cast(bytes, output)
parts.append(self.createFormPart("name=\"file\"; filename=\"%s\"" % file_name, output))
self._latest_reply_handler = self.postFormWithParts("print_jobs/", parts, onFinished=self._onPostPrintJobFinished, onProgress=self._onUploadPrintJobProgress)
self._latest_reply_handler = self.postFormWithParts("print_jobs/", parts, on_finished = self._onPostPrintJobFinished, on_progress = self._onUploadPrintJobProgress)
@pyqtProperty(QObject, notify=activePrinterChanged)
@pyqtProperty(QObject, notify = activePrinterChanged)
def activePrinter(self) -> Optional[PrinterOutputModel]:
return self._active_printer
@pyqtSlot(QObject)
def setActivePrinter(self, printer: Optional[PrinterOutputModel]):
def setActivePrinter(self, printer: Optional[PrinterOutputModel]) -> None:
if self._active_printer != printer:
if self._active_printer and self._active_printer.camera:
self._active_printer.camera.stop()
self._active_printer = printer
self.activePrinterChanged.emit()
def _onPostPrintJobFinished(self, reply):
def _onPostPrintJobFinished(self, reply: QNetworkReply) -> None:
self._progress_message.hide()
self._compressing_gcode = False
self._sending_gcode = False
def _onUploadPrintJobProgress(self, bytes_sent:int, bytes_total:int):
def _onUploadPrintJobProgress(self, bytes_sent: int, bytes_total: int) -> None:
if bytes_total > 0:
new_progress = bytes_sent / bytes_total * 100
# Treat upload progress as response. Uploading can take more than 10 seconds, so if we don't, we can get
# timeout responses if this happens.
self._last_response_time = time()
if new_progress > self._progress_message.getProgress():
if self._progress_message and new_progress > self._progress_message.getProgress():
self._progress_message.show() # Ensure that the message is visible.
self._progress_message.setProgress(bytes_sent / bytes_total * 100)
@ -272,16 +276,18 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._success_message.actionTriggered.connect(self._successMessageActionTriggered)
self._success_message.show()
else:
self._progress_message.setProgress(0)
self._progress_message.hide()
if self._progress_message is not None:
self._progress_message.setProgress(0)
self._progress_message.hide()
def _progressMessageActionTriggered(self, message_id: Optional[str]=None, action_id: Optional[str]=None) -> None:
def _progressMessageActionTriggered(self, message_id: Optional[str] = None, action_id: Optional[str] = None) -> None:
if action_id == "Abort":
Logger.log("d", "User aborted sending print to remote.")
self._progress_message.hide()
if self._progress_message is not None:
self._progress_message.hide()
self._compressing_gcode = False
self._sending_gcode = False
Application.getInstance().getController().setActiveStage("PrepareStage")
CuraApplication.getInstance().getController().setActiveStage("PrepareStage")
# After compressing the sliced model Cura sends data to printer, to stop receiving updates from the request
# the "reply" should be disconnected
@ -289,9 +295,9 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._latest_reply_handler.disconnect()
self._latest_reply_handler = None
def _successMessageActionTriggered(self, message_id: Optional[str]=None, action_id: Optional[str]=None) -> None:
def _successMessageActionTriggered(self, message_id: Optional[str] = None, action_id: Optional[str] = None) -> None:
if action_id == "View":
Application.getInstance().getController().setActiveStage("MonitorStage")
CuraApplication.getInstance().getController().setActiveStage("MonitorStage")
@pyqtSlot()
def openPrintJobControlPanel(self) -> None:
@ -303,21 +309,21 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
Logger.log("d", "Opening printer control panel...")
QDesktopServices.openUrl(QUrl("http://" + self._address + "/printers"))
@pyqtProperty("QVariantList", notify=printJobsChanged)
def printJobs(self)-> List[PrintJobOutputModel] :
@pyqtProperty("QVariantList", notify = printJobsChanged)
def printJobs(self)-> List[PrintJobOutputModel]:
return self._print_jobs
@pyqtProperty("QVariantList", notify=printJobsChanged)
@pyqtProperty("QVariantList", notify = printJobsChanged)
def queuedPrintJobs(self) -> List[PrintJobOutputModel]:
return [print_job for print_job in self._print_jobs if print_job.state == "queued"]
@pyqtProperty("QVariantList", notify=printJobsChanged)
@pyqtProperty("QVariantList", notify = printJobsChanged)
def activePrintJobs(self) -> List[PrintJobOutputModel]:
return [print_job for print_job in self._print_jobs if print_job.assignedPrinter is not None and print_job.state != "queued"]
@pyqtProperty("QVariantList", notify=clusterPrintersChanged)
def connectedPrintersTypeCount(self) -> List[PrinterOutputModel]:
printer_count = {}
@pyqtProperty("QVariantList", notify = clusterPrintersChanged)
def connectedPrintersTypeCount(self) -> List[Dict[str, str]]:
printer_count = {} # type: Dict[str, int]
for printer in self._printers:
if printer.type in printer_count:
printer_count[printer.type] += 1
@ -325,20 +331,20 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
printer_count[printer.type] = 1
result = []
for machine_type in printer_count:
result.append({"machine_type": machine_type, "count": printer_count[machine_type]})
result.append({"machine_type": machine_type, "count": str(printer_count[machine_type])})
return result
@pyqtSlot(int, result=str)
@pyqtSlot(int, result = str)
def formatDuration(self, seconds: int) -> str:
return Duration(seconds).getDisplayString(DurationFormat.Format.Short)
@pyqtSlot(int, result=str)
@pyqtSlot(int, result = str)
def getTimeCompleted(self, time_remaining: int) -> str:
current_time = time()
datetime_completed = datetime.fromtimestamp(current_time + time_remaining)
return "{hour:02d}:{minute:02d}".format(hour=datetime_completed.hour, minute=datetime_completed.minute)
@pyqtSlot(int, result=str)
@pyqtSlot(int, result = str)
def getDateCompleted(self, time_remaining: int) -> str:
current_time = time()
datetime_completed = datetime.fromtimestamp(current_time + time_remaining)
@ -373,10 +379,9 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self.sendMaterialProfiles()
def _update(self) -> None:
if not super()._update():
return
self.get("printers/", onFinished=self._onGetPrintersDataFinished)
self.get("print_jobs/", onFinished=self._onGetPrintJobsFinished)
super()._update()
self.get("printers/", on_finished = self._onGetPrintersDataFinished)
self.get("print_jobs/", on_finished = self._onGetPrintJobsFinished)
def _onGetPrintJobsFinished(self, reply: QNetworkReply) -> None:
if not checkValidGetReply(reply):
@ -415,7 +420,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
removed_jobs = [print_job for print_job in self._print_jobs if print_job not in print_jobs_seen]
for removed_job in removed_jobs:
job_list_changed |= self._removeJob(removed_job)
job_list_changed = job_list_changed or self._removeJob(removed_job)
if job_list_changed:
self.printJobsChanged.emit() # Do a single emit for all print job changes.
@ -449,27 +454,27 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
if removed_printers or printer_list_changed:
self.printersChanged.emit()
def _createPrinterModel(self, data: Dict) -> PrinterOutputModel:
printer = PrinterOutputModel(output_controller=ClusterUM3PrinterOutputController(self),
number_of_extruders=self._number_of_extruders)
def _createPrinterModel(self, data: Dict[str, Any]) -> PrinterOutputModel:
printer = PrinterOutputModel(output_controller = ClusterUM3PrinterOutputController(self),
number_of_extruders = self._number_of_extruders)
printer.setCamera(NetworkCamera("http://" + data["ip_address"] + ":8080/?action=stream"))
self._printers.append(printer)
return printer
def _createPrintJobModel(self, data: Dict) -> PrintJobOutputModel:
def _createPrintJobModel(self, data: Dict[str, Any]) -> PrintJobOutputModel:
print_job = PrintJobOutputModel(output_controller=ClusterUM3PrinterOutputController(self),
key=data["uuid"], name= data["name"])
print_job.stateChanged.connect(self._printJobStateChanged)
self._print_jobs.append(print_job)
return print_job
def _updatePrintJob(self, print_job: PrintJobOutputModel, data: Dict) -> None:
def _updatePrintJob(self, print_job: PrintJobOutputModel, data: Dict[str, Any]) -> None:
print_job.updateTimeTotal(data["time_total"])
print_job.updateTimeElapsed(data["time_elapsed"])
print_job.updateState(data["status"])
print_job.updateOwner(data["owner"])
def _updatePrinter(self, printer: PrinterOutputModel, data: Dict) -> None:
def _updatePrinter(self, printer: PrinterOutputModel, data: Dict[str, Any]) -> None:
# For some unknown reason the cluster wants UUID for everything, except for sending a job directly to a printer.
# Then we suddenly need the unique name. So in order to not have to mess up all the other code, we save a mapping.
self._printer_uuid_to_unique_name_mapping[data["uuid"]] = data["unique_name"]
@ -485,7 +490,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
printer.updateKey(data["uuid"])
printer.updateType(data["machine_variant"])
# Do not store the buildplate information that comes from connect if the current printer has not buildplate information
# Do not store the build plate information that comes from connect if the current printer has not build plate information
if "build_plate" in data and machine_definition.getMetaDataEntry("has_variant_buildplates", False):
printer.updateBuildplateName(data["build_plate"]["type"])
if not data["enabled"]:
@ -524,7 +529,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
brand=brand, color=color, name=name)
extruder.updateActiveMaterial(material)
def _removeJob(self, job: PrintJobOutputModel):
def _removeJob(self, job: PrintJobOutputModel) -> bool:
if job not in self._print_jobs:
return False
@ -535,7 +540,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
return True
def _removePrinter(self, printer: PrinterOutputModel):
def _removePrinter(self, printer: PrinterOutputModel) -> None:
self._printers.remove(printer)
if self._active_printer == printer:
self._active_printer = None
@ -549,16 +554,16 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
job = SendMaterialJob(device = self)
job.run()
def loadJsonFromReply(reply):
def loadJsonFromReply(reply: QNetworkReply) -> Optional[List[Dict[str, Any]]]:
try:
result = json.loads(bytes(reply.readAll()).decode("utf-8"))
except json.decoder.JSONDecodeError:
Logger.logException("w", "Unable to decode JSON from reply.")
return
return None
return result
def checkValidGetReply(reply):
def checkValidGetReply(reply: QNetworkReply) -> bool:
status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
if status_code != 200:
@ -567,7 +572,8 @@ def checkValidGetReply(reply):
return True
def findByKey(list, key):
def findByKey(list: List[Union[PrintJobOutputModel, PrinterOutputModel]], key: str) -> Optional[PrintJobOutputModel]:
for item in list:
if item.key == key:
return item
return None

View file

@ -1,43 +1,47 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os.path
import time
from typing import Optional
from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot, QObject
from UM.Application import Application
from UM.PluginRegistry import PluginRegistry
from UM.Logger import Logger
from UM.i18n import i18nCatalog
from cura.CuraApplication import CuraApplication
from cura.MachineAction import MachineAction
from .UM3OutputDevicePlugin import UM3OutputDevicePlugin
catalog = i18nCatalog("cura")
class DiscoverUM3Action(MachineAction):
discoveredDevicesChanged = pyqtSignal()
def __init__(self):
def __init__(self) -> None:
super().__init__("DiscoverUM3Action", catalog.i18nc("@action","Connect via Network"))
self._qml_url = "DiscoverUM3Action.qml"
self._network_plugin = None
self._network_plugin = None #type: Optional[UM3OutputDevicePlugin]
self.__additional_components_context = None
self.__additional_component = None
self.__additional_components_view = None
self.__additional_components_view = None #type: Optional[QObject]
Application.getInstance().engineCreatedSignal.connect(self._createAdditionalComponentsView)
CuraApplication.getInstance().engineCreatedSignal.connect(self._createAdditionalComponentsView)
self._last_zero_conf_event_time = time.time()
self._last_zero_conf_event_time = time.time() #type: float
# Time to wait after a zero-conf service change before allowing a zeroconf reset
self._zero_conf_change_grace_period = 0.25
self._zero_conf_change_grace_period = 0.25 #type: float
@pyqtSlot()
def startDiscovery(self):
if not self._network_plugin:
Logger.log("d", "Starting device discovery.")
self._network_plugin = Application.getInstance().getOutputDeviceManager().getOutputDevicePlugin("UM3NetworkPrinting")
self._network_plugin = CuraApplication.getInstance().getOutputDeviceManager().getOutputDevicePlugin("UM3NetworkPrinting")
self._network_plugin.discoveredDevicesChanged.connect(self._onDeviceDiscoveryChanged)
self.discoveredDevicesChanged.emit()
@ -93,16 +97,16 @@ class DiscoverUM3Action(MachineAction):
return []
@pyqtSlot(str)
def setGroupName(self, group_name):
def setGroupName(self, group_name: str) -> None:
Logger.log("d", "Attempting to set the group name of the active machine to %s", group_name)
global_container_stack = Application.getInstance().getGlobalContainerStack()
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
if global_container_stack:
meta_data = global_container_stack.getMetaData()
if "connect_group_name" in meta_data:
previous_connect_group_name = meta_data["connect_group_name"]
global_container_stack.setMetaDataEntry("connect_group_name", group_name)
# Find all the places where there is the same group name and change it accordingly
Application.getInstance().getMachineManager().replaceContainersMetadata(key = "connect_group_name", value = previous_connect_group_name, new_value = group_name)
CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "connect_group_name", value = previous_connect_group_name, new_value = group_name)
else:
global_container_stack.addMetaDataEntry("connect_group_name", group_name)
global_container_stack.addMetaDataEntry("hidden", False)
@ -112,9 +116,9 @@ class DiscoverUM3Action(MachineAction):
self._network_plugin.reCheckConnections()
@pyqtSlot(str)
def setKey(self, key):
def setKey(self, key: str) -> None:
Logger.log("d", "Attempting to set the network key of the active machine to %s", key)
global_container_stack = Application.getInstance().getGlobalContainerStack()
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
if global_container_stack:
meta_data = global_container_stack.getMetaData()
if "um_network_key" in meta_data:
@ -124,7 +128,7 @@ class DiscoverUM3Action(MachineAction):
Logger.log("d", "Removing old authentication id %s for device %s", global_container_stack.getMetaDataEntry("network_authentication_id", None), key)
global_container_stack.removeMetaDataEntry("network_authentication_id")
global_container_stack.removeMetaDataEntry("network_authentication_key")
Application.getInstance().getMachineManager().replaceContainersMetadata(key = "um_network_key", value = previous_network_key, new_value = key)
CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "um_network_key", value = previous_network_key, new_value = key)
else:
global_container_stack.addMetaDataEntry("um_network_key", key)
@ -134,7 +138,7 @@ class DiscoverUM3Action(MachineAction):
@pyqtSlot(result = str)
def getStoredKey(self) -> str:
global_container_stack = Application.getInstance().getGlobalContainerStack()
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
if global_container_stack:
meta_data = global_container_stack.getMetaData()
if "um_network_key" in meta_data:
@ -149,12 +153,12 @@ class DiscoverUM3Action(MachineAction):
return ""
@pyqtSlot(str, result = bool)
def existsKey(self, key) -> bool:
return Application.getInstance().getMachineManager().existNetworkInstances(network_key = key)
def existsKey(self, key: str) -> bool:
return CuraApplication.getInstance().getMachineManager().existNetworkInstances(network_key = key)
@pyqtSlot()
def loadConfigurationFromPrinter(self):
machine_manager = Application.getInstance().getMachineManager()
def loadConfigurationFromPrinter(self) -> None:
machine_manager = CuraApplication.getInstance().getMachineManager()
hotend_ids = machine_manager.printerOutputDevices[0].hotendIds
for index in range(len(hotend_ids)):
machine_manager.printerOutputDevices[0].hotendIdChanged.emit(index, hotend_ids[index])
@ -162,16 +166,16 @@ class DiscoverUM3Action(MachineAction):
for index in range(len(material_ids)):
machine_manager.printerOutputDevices[0].materialIdChanged.emit(index, material_ids[index])
def _createAdditionalComponentsView(self):
def _createAdditionalComponentsView(self) -> None:
Logger.log("d", "Creating additional ui components for UM3.")
# Create networking dialog
path = os.path.join(PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), "UM3InfoComponents.qml")
self.__additional_components_view = Application.getInstance().createQmlComponent(path, {"manager": self})
self.__additional_components_view = CuraApplication.getInstance().createQmlComponent(path, {"manager": self})
if not self.__additional_components_view:
Logger.log("w", "Could not create ui components for UM3.")
return
# Create extra components
Application.getInstance().addAdditionalComponent("monitorButtons", self.__additional_components_view.findChild(QObject, "networkPrinterConnectButton"))
Application.getInstance().addAdditionalComponent("machinesDetailPane", self.__additional_components_view.findChild(QObject, "networkPrinterConnectionInfo"))
CuraApplication.getInstance().addAdditionalComponent("monitorButtons", self.__additional_components_view.findChild(QObject, "networkPrinterConnectButton"))
CuraApplication.getInstance().addAdditionalComponent("machinesDetailPane", self.__additional_components_view.findChild(QObject, "networkPrinterConnectionInfo"))

View file

@ -1,3 +1,8 @@
from typing import List, Optional
from UM.FileHandler.FileHandler import FileHandler
from UM.Scene.SceneNode import SceneNode
from cura.CuraApplication import CuraApplication
from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
@ -9,12 +14,11 @@ from cura.Settings.ExtruderManager import ExtruderManager
from UM.Logger import Logger
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Application import Application
from UM.i18n import i18nCatalog
from UM.Message import Message
from PyQt5.QtNetwork import QNetworkRequest
from PyQt5.QtCore import QTimer, QCoreApplication
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QMessageBox
from .LegacyUM3PrinterOutputController import LegacyUM3PrinterOutputController
@ -39,7 +43,7 @@ i18n_catalog = i18nCatalog("cura")
# 4. At this point the machine either has the state Authenticated or AuthenticationDenied.
# 5. As a final step, we verify the authentication, as this forces the QT manager to setup the authenticator.
class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
def __init__(self, device_id, address: str, properties, parent = None):
def __init__(self, device_id, address: str, properties, parent = None) -> None:
super().__init__(device_id = device_id, address = address, properties = properties, parent = parent)
self._api_prefix = "/api/v1/"
self._number_of_extruders = 2
@ -125,7 +129,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
def connect(self):
super().connect()
self._setupMessages()
global_container = Application.getInstance().getGlobalContainerStack()
global_container = CuraApplication.getInstance().getGlobalContainerStack()
if global_container:
self._authentication_id = global_container.getMetaDataEntry("network_authentication_id", None)
self._authentication_key = global_container.getMetaDataEntry("network_authentication_key", None)
@ -168,7 +172,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
# NotImplementedError. We can simply ignore these.
pass
def requestWrite(self, nodes, file_name=None, filter_by_machine=False, file_handler=None, **kwargs):
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
if not self.activePrinter:
# No active printer. Unable to write
return
@ -183,8 +187,8 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
self.writeStarted.emit(self)
gcode_dict = getattr(Application.getInstance().getController().getScene(), "gcode_dict", [])
active_build_plate_id = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
gcode_dict = getattr(CuraApplication.getInstance().getController().getScene(), "gcode_dict", [])
active_build_plate_id = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
gcode_list = gcode_dict[active_build_plate_id]
if not gcode_list:
@ -203,7 +207,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
for error in errors:
detailed_text += error + "\n"
Application.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Mismatched configuration"),
CuraApplication.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Mismatched configuration"),
text,
informative_text,
detailed_text,
@ -225,7 +229,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
for warning in warnings:
detailed_text += warning + "\n"
Application.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Mismatched configuration"),
CuraApplication.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Mismatched configuration"),
text,
informative_text,
detailed_text,
@ -239,7 +243,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
self._startPrint()
# Notify the UI that a switch to the print monitor should happen
Application.getInstance().getController().setActiveStage("MonitorStage")
CuraApplication.getInstance().getController().setActiveStage("MonitorStage")
def _startPrint(self):
Logger.log("i", "Sending print job to printer.")
@ -264,7 +268,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
# Abort was called.
return
file_name = "%s.gcode.gz" % Application.getInstance().getPrintInformation().jobName
file_name = "%s.gcode.gz" % CuraApplication.getInstance().getPrintInformation().jobName
self.postForm("print_job", "form-data; name=\"file\";filename=\"%s\"" % file_name, compressed_gcode,
onFinished=self._onPostPrintJobFinished)
@ -276,7 +280,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
self._progress_message.hide()
self._compressing_gcode = False
self._sending_gcode = False
Application.getInstance().getController().setActiveStage("PrepareStage")
CuraApplication.getInstance().getController().setActiveStage("PrepareStage")
def _onPostPrintJobFinished(self, reply):
self._progress_message.hide()
@ -301,7 +305,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
if button == QMessageBox.Yes:
self._startPrint()
else:
Application.getInstance().getController().setActiveStage("PrepareStage")
CuraApplication.getInstance().getController().setActiveStage("PrepareStage")
# For some unknown reason Cura on OSX will hang if we do the call back code
# immediately without first returning and leaving QML's event system.
@ -309,7 +313,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
def _checkForErrors(self):
errors = []
print_information = Application.getInstance().getPrintInformation()
print_information = CuraApplication.getInstance().getPrintInformation()
if not print_information.materialLengths:
Logger.log("w", "There is no material length information. Unable to check for errors.")
return errors
@ -329,7 +333,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
def _checkForWarnings(self):
warnings = []
print_information = Application.getInstance().getPrintInformation()
print_information = CuraApplication.getInstance().getPrintInformation()
if not print_information.materialLengths:
Logger.log("w", "There is no material length information. Unable to check for warnings.")
@ -452,7 +456,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
self._authentication_failed_message.show()
def _saveAuthentication(self):
global_container_stack = Application.getInstance().getGlobalContainerStack()
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
if global_container_stack:
if "network_authentication_key" in global_container_stack.getMetaData():
global_container_stack.setMetaDataEntry("network_authentication_key", self._authentication_key)
@ -465,7 +469,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
global_container_stack.addMetaDataEntry("network_authentication_id", self._authentication_id)
# Force save so we are sure the data is not lost.
Application.getInstance().saveStack(global_container_stack)
CuraApplication.getInstance().saveStack(global_container_stack)
Logger.log("i", "Authentication succeeded for id %s and key %s", self._authentication_id,
self._getSafeAuthKey())
else:
@ -496,7 +500,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
self._authentication_id = None
self.post("auth/request",
json.dumps({"application": "Cura-" + Application.getInstance().getVersion(),
json.dumps({"application": "Cura-" + CuraApplication.getInstance().getVersion(),
"user": self._getUserName()}).encode(),
onFinished=self._onRequestAuthenticationFinished)
@ -542,7 +546,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
"Got status code {status_code} while trying to get printer data".format(status_code=status_code))
def materialHotendChangedMessage(self, callback):
Application.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Sync with your printer"),
CuraApplication.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Sync with your printer"),
i18n_catalog.i18nc("@label",
"Would you like to use your current printer configuration in Cura?"),
i18n_catalog.i18nc("@label",

View file

@ -3,10 +3,10 @@
from UM.Logger import Logger
from UM.i18n import i18nCatalog
from UM.Application import Application
from UM.Qt.Duration import DurationFormat
from UM.PluginRegistry import PluginRegistry
from cura.CuraApplication import CuraApplication
from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
@ -22,7 +22,7 @@ from threading import Thread, Event
from time import time, sleep
from queue import Queue
from enum import IntEnum
from typing import Union, Optional, List
from typing import Union, Optional, List, cast
import re
import functools # Used for reduce
@ -35,7 +35,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
firmwareProgressChanged = pyqtSignal()
firmwareUpdateStateChanged = pyqtSignal()
def __init__(self, serial_port: str, baud_rate: Optional[int] = None):
def __init__(self, serial_port: str, baud_rate: Optional[int] = None) -> None:
super().__init__(serial_port)
self.setName(catalog.i18nc("@item:inmenu", "USB printing"))
self.setShortDescription(catalog.i18nc("@action:button Preceded by 'Ready to'.", "Print via USB"))
@ -68,7 +68,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._is_printing = False # A print is being sent.
## Set when print is started in order to check running time.
self._print_start_time = None # type: Optional[int]
self._print_start_time = None # type: Optional[float]
self._print_estimated_time = None # type: Optional[int]
self._accepts_commands = True
@ -83,7 +83,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self.setConnectionText(catalog.i18nc("@info:status", "Connected via USB"))
# Queue for commands that need to be sent.
self._command_queue = Queue()
self._command_queue = Queue() # type: Queue
# Event to indicate that an "ok" was received from the printer after sending a command.
self._command_received = Event()
self._command_received.set()
@ -107,11 +107,11 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
# cancel any ongoing preheat timer before starting a print
self._printers[0].getController().stopPreheatTimers()
Application.getInstance().getController().setActiveStage("MonitorStage")
CuraApplication.getInstance().getController().setActiveStage("MonitorStage")
# find the G-code for the active build plate to print
active_build_plate_id = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
gcode_dict = getattr(Application.getInstance().getController().getScene(), "gcode_dict")
active_build_plate_id = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
gcode_dict = getattr(CuraApplication.getInstance().getController().getScene(), "gcode_dict")
gcode_list = gcode_dict[active_build_plate_id]
self._printGCode(gcode_list)
@ -121,7 +121,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
def showFirmwareInterface(self):
if self._firmware_view is None:
path = os.path.join(PluginRegistry.getInstance().getPluginPath("USBPrinting"), "FirmwareUpdateWindow.qml")
self._firmware_view = Application.getInstance().createQmlComponent(path, {"manager": self})
self._firmware_view = CuraApplication.getInstance().createQmlComponent(path, {"manager": self})
self._firmware_view.show()
@ -180,7 +180,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self.setFirmwareUpdateState(FirmwareUpdateState.completed)
# Try to re-connect with the machine again, which must be done on the Qt thread, so we use call later.
Application.getInstance().callLater(self.connect)
CuraApplication.getInstance().callLater(self.connect)
@pyqtProperty(float, notify = firmwareProgressChanged)
def firmwareProgress(self):
@ -214,7 +214,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._gcode_position = 0
self._print_start_time = time()
self._print_estimated_time = int(Application.getInstance().getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.Seconds))
self._print_estimated_time = int(CuraApplication.getInstance().getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.Seconds))
for i in range(0, 4): # Push first 4 entries before accepting other inputs
self._sendNextGcodeLine()
@ -250,7 +250,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
except SerialException:
Logger.log("w", "An exception occured while trying to create serial connection")
return
container_stack = Application.getInstance().getGlobalContainerStack()
container_stack = CuraApplication.getInstance().getGlobalContainerStack()
num_extruders = container_stack.getProperty("machine_extruder_count", "value")
# Ensure that a printer is created.
self._printers = [PrinterOutputModel(output_controller=GenericOutputController(self), number_of_extruders=num_extruders)]
@ -277,13 +277,12 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
if self._serial is None or self._connection_state != ConnectionState.connected:
return
if type(command == str):
command = command.encode()
if not command.endswith(b"\n"):
command += b"\n"
new_command = cast(bytes, command) if type(command) is bytes else cast(str, command).encode() # type: bytes
if not new_command.endswith(b"\n"):
new_command += b"\n"
try:
self._command_received.clear()
self._serial.write(command)
self._serial.write(new_command)
except SerialTimeoutException:
Logger.log("w", "Timeout when sending command to printer via USB.")
self._command_received.set()

View file

@ -179,7 +179,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin):
return list(base_list)
__instance = None
__instance = None # type: USBPrinterOutputDeviceManager
@classmethod
def getInstance(cls, *args, **kwargs) -> "USBPrinterOutputDeviceManager":

View file

@ -44,20 +44,18 @@ class Profile:
# Parse the general section.
self._name = parser.get("general", "name")
self._type = parser.get("general", "type", fallback = None)
self._type = parser.get("general", "type")
self._weight = None
if "weight" in parser["general"]:
self._weight = int(parser.get("general", "weight"))
else:
self._weight = None
self._machine_type_id = parser.get("general", "machine_type", fallback = None)
self._machine_variant_name = parser.get("general", "machine_variant", fallback = None)
self._machine_instance_name = parser.get("general", "machine_instance", fallback = None)
self._machine_type_id = parser.get("general", "machine_type")
self._machine_variant_name = parser.get("general", "machine_variant")
self._machine_instance_name = parser.get("general", "machine_instance")
self._material_name = None
if "material" in parser["general"]: #Note: Material name is unused in this upgrade.
self._material_name = parser.get("general", "material")
elif self._type == "material":
self._material_name = parser.get("general", "name", fallback = None)
else:
self._material_name = None
self._material_name = parser.get("general", "name")
# Parse the settings.
self._settings = {} # type: Dict[str,str]

View file

@ -0,0 +1,91 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import configparser
import io
from UM.VersionUpgrade import VersionUpgrade
## Upgrades configurations from the state they were in at version 3.4 to the
# state they should be in at version 4.0.
class VersionUpgrade34to40(VersionUpgrade):
## Gets the version number from a CFG file in Uranium's 3.3 format.
#
# Since the format may change, this is implemented for the 3.3 format only
# and needs to be included in the version upgrade system rather than
# globally in Uranium.
#
# \param serialised The serialised form of a CFG file.
# \return The version number stored in the CFG file.
# \raises ValueError The format of the version number in the file is
# incorrect.
# \raises KeyError The format of the file is incorrect.
def getCfgVersion(self, serialised):
parser = configparser.ConfigParser(interpolation = None)
parser.read_string(serialised)
format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
setting_version = int(parser.get("metadata", "setting_version", fallback = 0))
return format_version * 1000000 + setting_version
## Upgrades Preferences to have the new version number.
def upgradePreferences(self, serialized, filename):
parser = configparser.ConfigParser(interpolation = None)
parser.read_string(serialized)
# Update version number.
parser["general"]["version"] = "4"
if "metadata" not in parser:
parser["metadata"] = {}
parser["metadata"]["setting_version"] = "5"
result = io.StringIO()
parser.write(result)
return [filename], [result.getvalue()]
## Upgrades stacks to have the new version number.
def upgradeStack(self, serialized, filename):
parser = configparser.ConfigParser(interpolation = None)
parser.read_string(serialized)
# Update version number.
parser["general"]["version"] = "4"
parser["metadata"]["setting_version"] = "5"
result = io.StringIO()
parser.write(result)
return [filename], [result.getvalue()]
## Upgrades instance containers to have the new version
# number.
def upgradeInstanceContainer(self, serialized, filename):
parser = configparser.ConfigParser(interpolation = None)
parser.read_string(serialized)
# Update version number.
parser["general"]["version"] = "4"
parser["metadata"]["setting_version"] = "5"
self._resetConcentric3DInfillPattern(parser)
result = io.StringIO()
parser.write(result)
return [filename], [result.getvalue()]
def _resetConcentric3DInfillPattern(self, parser):
if "values" not in parser:
return
# Reset the patterns which are concentric 3d
for key in ("infill_pattern",
"support_pattern",
"support_interface_pattern",
"support_roof_pattern",
"support_bottom_pattern",
):
if key not in parser["values"]:
continue
if parser["values"][key] == "concentric_3d":
del parser["values"][key]

View file

@ -0,0 +1,52 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from . import VersionUpgrade34to40
upgrade = VersionUpgrade34to40.VersionUpgrade34to40()
def getMetaData():
return {
"version_upgrade": {
# From To Upgrade function
("preferences", 6000004): ("preferences", 6000005, upgrade.upgradePreferences),
("definition_changes", 4000004): ("definition_changes", 4000005, upgrade.upgradeInstanceContainer),
("quality_changes", 4000004): ("quality_changes", 4000005, upgrade.upgradeInstanceContainer),
("user", 4000004): ("user", 4000005, upgrade.upgradeInstanceContainer),
("machine_stack", 4000005): ("machine_stack", 4000005, upgrade.upgradeStack),
("extruder_train", 4000005): ("extruder_train", 4000005, upgrade.upgradeStack),
},
"sources": {
"preferences": {
"get_version": upgrade.getCfgVersion,
"location": {"."}
},
"machine_stack": {
"get_version": upgrade.getCfgVersion,
"location": {"./machine_instances"}
},
"extruder_train": {
"get_version": upgrade.getCfgVersion,
"location": {"./extruders"}
},
"definition_changes": {
"get_version": upgrade.getCfgVersion,
"location": {"./definition_changes"}
},
"quality_changes": {
"get_version": upgrade.getCfgVersion,
"location": {"./quality_changes"}
},
"user": {
"get_version": upgrade.getCfgVersion,
"location": {"./user"}
}
}
}
def register(app):
return { "version_upgrade": upgrade }

View file

@ -0,0 +1,8 @@
{
"name": "Version Upgrade 3.4 to 4.0",
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 3.4 to Cura 4.0.",
"api": 4,
"i18n-catalog": "cura"
}

View file

@ -26,8 +26,8 @@ except ImportError:
DEFAULT_SUBDIV = 16 # Default subdivision factor for spheres, cones, and cylinders
EPSILON = 0.000001
class Shape:
class Shape:
# Expects verts in MeshBuilder-ready format, as a n by 3 mdarray
# with vertices stored in rows
def __init__(self, verts, faces, index_base, name):
@ -37,9 +37,10 @@ class Shape:
self.index_base = index_base
self.name = name
class X3DReader(MeshReader):
def __init__(self, application):
super().__init__(application)
def __init__(self) -> None:
super().__init__()
self._supported_extensions = [".x3d"]
self._namespaces = {}

View file

@ -15,5 +15,6 @@ def getMetaData():
]
}
def register(app):
return { "mesh_reader": X3DReader.X3DReader(app) }
return {"mesh_reader": X3DReader.X3DReader()}

View file

@ -6,8 +6,10 @@ import io
import json #To parse the product-to-id mapping file.
import os.path #To find the product-to-id mapping.
import sys
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, cast
import xml.etree.ElementTree as ET
from typing import Dict
from typing import Iterator
from UM.Resources import Resources
from UM.Logger import Logger
@ -132,7 +134,7 @@ class XmlMaterialProfile(InstanceContainer):
"version": self.CurrentFdmMaterialVersion})
## Begin Metadata Block
builder.start("metadata")
builder.start("metadata") # type: ignore
metadata = copy.deepcopy(self.getMetaData())
# setting_version is derived from the "version" tag in the schema, so don't serialize it into a file
@ -156,21 +158,21 @@ class XmlMaterialProfile(InstanceContainer):
metadata.pop("name", "")
## Begin Name Block
builder.start("name")
builder.start("name") # type: ignore
builder.start("brand")
builder.start("brand") # type: ignore
builder.data(metadata.pop("brand", ""))
builder.end("brand")
builder.start("material")
builder.start("material") # type: ignore
builder.data(metadata.pop("material", ""))
builder.end("material")
builder.start("color")
builder.start("color") # type: ignore
builder.data(metadata.pop("color_name", ""))
builder.end("color")
builder.start("label")
builder.start("label") # type: ignore
builder.data(self.getName())
builder.end("label")
@ -178,7 +180,7 @@ class XmlMaterialProfile(InstanceContainer):
## End Name Block
for key, value in metadata.items():
builder.start(key)
builder.start(key) # type: ignore
if value is not None: #Nones get handled well by the builder.
#Otherwise the builder always expects a string.
#Deserialize expects the stringified version.
@ -190,10 +192,10 @@ class XmlMaterialProfile(InstanceContainer):
## End Metadata Block
## Begin Properties Block
builder.start("properties")
builder.start("properties") # type: ignore
for key, value in properties.items():
builder.start(key)
builder.start(key) # type: ignore
builder.data(value)
builder.end(key)
@ -201,14 +203,14 @@ class XmlMaterialProfile(InstanceContainer):
## End Properties Block
## Begin Settings Block
builder.start("settings")
builder.start("settings") # type: ignore
if self.getMetaDataEntry("definition") == "fdmprinter":
for instance in self.findInstances():
self._addSettingElement(builder, instance)
machine_container_map = {}
machine_variant_map = {}
machine_container_map = {} # type: Dict[str, InstanceContainer]
machine_variant_map = {} # type: Dict[str, Dict[str, Any]]
variant_manager = CuraApplication.getInstance().getVariantManager()
@ -248,7 +250,7 @@ class XmlMaterialProfile(InstanceContainer):
product = product_name
break
builder.start("machine")
builder.start("machine") # type: ignore
builder.start("machine_identifier", {
"manufacturer": container.getMetaDataEntry("machine_manufacturer",
definition_metadata.get("manufacturer", "Unknown")),
@ -264,7 +266,7 @@ class XmlMaterialProfile(InstanceContainer):
self._addSettingElement(builder, instance)
# Find all hotend sub-profiles corresponding to this material and machine and add them to this profile.
buildplate_dict = {}
buildplate_dict = {} # type: Dict[str, Any]
for variant_name, variant_dict in machine_variant_map[definition_id].items():
variant_type = variant_dict["variant_node"].metadata["hardware_type"]
from cura.Machines.VariantManager import VariantType
@ -839,11 +841,14 @@ class XmlMaterialProfile(InstanceContainer):
if label is not None and label.text is not None:
base_metadata["name"] = label.text
else:
base_metadata["name"] = cls._profile_name(material.text, color.text)
if material is not None and color is not None:
base_metadata["name"] = cls._profile_name(material.text, color.text)
else:
base_metadata["name"] = "Unknown Material"
base_metadata["brand"] = brand.text if brand.text is not None else "Unknown Brand"
base_metadata["material"] = material.text if material.text is not None else "Unknown Type"
base_metadata["color_name"] = color.text if color.text is not None else "Unknown Color"
base_metadata["brand"] = brand.text if brand is not None and brand.text is not None else "Unknown Brand"
base_metadata["material"] = material.text if material is not None and material.text is not None else "Unknown Type"
base_metadata["color_name"] = color.text if color is not None and color.text is not None else "Unknown Color"
continue
#Setting_version is derived from the "version" tag in the schema earlier, so don't set it here.
@ -863,13 +868,13 @@ class XmlMaterialProfile(InstanceContainer):
tag_name = _tag_without_namespace(entry)
property_values[tag_name] = entry.text
base_metadata["approximate_diameter"] = str(round(float(property_values.get("diameter", 2.85)))) # In mm
base_metadata["approximate_diameter"] = str(round(float(cast(float, property_values.get("diameter", 2.85))))) # In mm
base_metadata["properties"] = property_values
base_metadata["definition"] = "fdmprinter"
compatible_entries = data.iterfind("./um:settings/um:setting[@key='hardware compatible']", cls.__namespaces)
try:
common_compatibility = cls._parseCompatibleValue(next(compatible_entries).text)
common_compatibility = cls._parseCompatibleValue(next(compatible_entries).text) # type: ignore
except StopIteration: #No 'hardware compatible' setting.
common_compatibility = True
base_metadata["compatible"] = common_compatibility
@ -883,7 +888,8 @@ class XmlMaterialProfile(InstanceContainer):
for entry in machine.iterfind("./um:setting", cls.__namespaces):
key = entry.get("key")
if key == "hardware compatible":
machine_compatibility = cls._parseCompatibleValue(entry.text)
if entry.text is not None:
machine_compatibility = cls._parseCompatibleValue(entry.text)
for identifier in machine.iterfind("./um:machine_identifier", cls.__namespaces):
machine_id_list = product_id_map.get(identifier.get("product"), [])
@ -891,11 +897,11 @@ class XmlMaterialProfile(InstanceContainer):
machine_id_list = cls.getPossibleDefinitionIDsFromName(identifier.get("product"))
for machine_id in machine_id_list:
definition_metadata = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = machine_id)
if not definition_metadata:
definition_metadatas = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = machine_id)
if not definition_metadatas:
continue
definition_metadata = definition_metadata[0]
definition_metadata = definition_metadatas[0]
machine_manufacturer = identifier.get("manufacturer", definition_metadata.get("manufacturer", "Unknown")) #If the XML material doesn't specify a manufacturer, use the one in the actual printer definition.
@ -918,7 +924,7 @@ class XmlMaterialProfile(InstanceContainer):
result_metadata.append(new_material_metadata)
buildplates = machine.iterfind("./um:buildplate", cls.__namespaces)
buildplate_map = {}
buildplate_map = {} # type: Dict[str, Dict[str, bool]]
buildplate_map["buildplate_compatible"] = {}
buildplate_map["buildplate_recommended"] = {}
for buildplate in buildplates:
@ -939,10 +945,11 @@ class XmlMaterialProfile(InstanceContainer):
buildplate_recommended = True
for entry in settings:
key = entry.get("key")
if key == "hardware compatible":
buildplate_compatibility = cls._parseCompatibleValue(entry.text)
elif key == "hardware recommended":
buildplate_recommended = cls._parseCompatibleValue(entry.text)
if entry.text is not None:
if key == "hardware compatible":
buildplate_compatibility = cls._parseCompatibleValue(entry.text)
elif key == "hardware recommended":
buildplate_recommended = cls._parseCompatibleValue(entry.text)
buildplate_map["buildplate_compatible"][buildplate_id] = buildplate_compatibility
buildplate_map["buildplate_recommended"][buildplate_id] = buildplate_recommended
@ -956,7 +963,8 @@ class XmlMaterialProfile(InstanceContainer):
for entry in hotend.iterfind("./um:setting", cls.__namespaces):
key = entry.get("key")
if key == "hardware compatible":
hotend_compatibility = cls._parseCompatibleValue(entry.text)
if entry.text is not None:
hotend_compatibility = cls._parseCompatibleValue(entry.text)
new_hotend_specific_material_id = container_id + "_" + machine_id + "_" + hotend_name.replace(" ", "_")

View file

@ -0,0 +1,5 @@
{
"name": "Creality CR-10S",
"version": 2,
"inherits": "creality_cr10"
}

View file

@ -1627,7 +1627,6 @@
"tetrahedral": "Octet",
"quarter_cubic": "Quarter Cubic",
"concentric": "Concentric",
"concentric_3d": "Concentric 3D",
"zigzag": "Zig Zag",
"cross": "Cross",
"cross_3d": "Cross 3D"
@ -1655,7 +1654,7 @@
"description": "A list of integer line directions to use. Elements from the list are used sequentially as the layers progress and when the end of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained in square brackets. Default is an empty list which means use the traditional default angles (45 and 135 degrees for the lines and zig zag patterns and 45 degrees for all other patterns).",
"type": "[int]",
"default_value": "[ ]",
"enabled": "infill_pattern != 'concentric' and infill_pattern != 'concentric_3d' and infill_pattern != 'cubicsubdiv' and infill_sparse_density > 0",
"enabled": "infill_pattern != 'concentric' and infill_pattern != 'cubicsubdiv' and infill_sparse_density > 0",
"limit_to_extruder": "infill_extruder_nr",
"settable_per_mesh": true
},
@ -1791,7 +1790,7 @@
"default_value": 0,
"type": "int",
"minimum_value": "0",
"maximum_value_warning": "1 if (infill_pattern == 'cross' or infill_pattern == 'cross_3d' or support_pattern == 'concentric' or support_pattern == 'concentric_3d') else 5",
"maximum_value_warning": "1 if (infill_pattern == 'cross' or infill_pattern == 'cross_3d' or support_pattern == 'concentric') else 5",
"maximum_value": "0 if spaghetti_infill_enabled else (999999 if infill_line_distance == 0 else (20 - math.log(infill_line_distance) / math.log(2)))",
"enabled": "infill_sparse_density > 0 and infill_pattern != 'cubicsubdiv' and not spaghetti_infill_enabled",
"limit_to_extruder": "infill_extruder_nr",
@ -3750,7 +3749,6 @@
"grid": "Grid",
"triangles": "Triangles",
"concentric": "Concentric",
"concentric_3d": "Concentric 3D",
"zigzag": "Zig Zag",
"cross": "Cross"
},
@ -3990,7 +3988,7 @@
"default_value": 0,
"type": "int",
"minimum_value": "0",
"maximum_value_warning": "1 if (support_pattern == 'cross' or support_pattern == 'lines' or support_pattern == 'zigzag' or support_pattern == 'concentric' or support_pattern == 'concentric_3d') else 5",
"maximum_value_warning": "1 if (support_pattern == 'cross' or support_pattern == 'lines' or support_pattern == 'zigzag' or support_pattern == 'concentric') else 5",
"maximum_value": "999999 if support_line_distance == 0 else (20 - math.log(support_line_distance) / math.log(2))",
"enabled": "(support_enable or support_tree_enable) and support_infill_rate > 0",
"limit_to_extruder": "support_infill_extruder_nr",
@ -4197,7 +4195,6 @@
"grid": "Grid",
"triangles": "Triangles",
"concentric": "Concentric",
"concentric_3d": "Concentric 3D",
"zigzag": "Zig Zag"
},
"default_value": "concentric",
@ -4218,7 +4215,6 @@
"grid": "Grid",
"triangles": "Triangles",
"concentric": "Concentric",
"concentric_3d": "Concentric 3D",
"zigzag": "Zig Zag"
},
"default_value": "concentric",
@ -4239,7 +4235,6 @@
"grid": "Grid",
"triangles": "Triangles",
"concentric": "Concentric",
"concentric_3d": "Concentric 3D",
"zigzag": "Zig Zag"
},
"default_value": "concentric",
@ -5438,7 +5433,7 @@
"unit": "mm",
"type": "float",
"minimum_value": "0.001",
"default_value": 4,
"default_value": 1,
"limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_tree_enable",
"settable_per_mesh": true

View file

@ -0,0 +1,101 @@
{
"name": "Tevo Tornado",
"version": 2,
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "nean",
"manufacturer": "Tevo",
"file_formats": "text/x-gcode",
"icon": "icon_ultimaker2.png",
"has_materials": true
},
"overrides": {
"machine_name": {
"default_value": "Tevo Tornado"
},
"machine_width": {
"default_value": 300
},
"machine_height": {
"default_value": 400
},
"machine_depth": {
"default_value": 300
},
"machine_center_is_zero": {
"default_value": false
},
"machine_head_polygon": {
"default_value": [
[-30, 34],
[-30, -32],
[30, -32],
[30, 34]
]
},
"material_diameter": {
"default_value": 1.75
},
"machine_nozzle_size": {
"default_value": 0.4
},
"top_bottom_thickness": {
"default_value": 1.2
},
"top_bottom_pattern": {
"default_value": "lines"
},
"infill_pattern": {
"value": "'triangles'"
},
"retraction_enable": {
"default_value": true
},
"retraction_amount": {
"default_value": 6.8
},
"retraction_speed": {
"default_value": 40
},
"cool_min_layer_time": {
"default_value": 10
},
"adhesion_type": {
"default_value": "skirt"
},
"skirt_line_count": {
"default_value": 4
},
"skirt_gap": {
"default_value": 10
},
"machine_heated_bed": {
"default_value": true
},
"gantry_height": {
"default_value": 30
},
"acceleration_enabled": {
"default_value": false
},
"machine_acceleration": {
"default_value": 1500
},
"jerk_enabled": {
"default_value": false
},
"machine_max_jerk_xy": {
"default_value": 6
},
"machine_gcode_flavor": {
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
"default_value": "; start_gcode\nM117 Start Clean ; Indicate nozzle clean in progress on LCD\n;\nM104 S[extruder0_temperature] \nM109 S[extruder0_temperature] \nM109 R[extruder0_temperature] \n;\nM107 ; Turn layer fan off\nG21 ; Set to metric [change to G20 if you want Imperial]\nG90 ; Force coordinates to be absolute relative to the origin\nG28 ; Home X/Y/Z axis\n;\nG1 X3 Y1 Z15 F9000 ; Move safe Z height to shear strings\nG0 X1 Y1 Z0.2 F9000 ; Move in 1mm from edge and up [z] 0.2mm\nG92 E0 ; Set extruder to [0] zero\nG1 X100 E12 F500 ; Extrude 30mm filiment along X axis 100mm long to prime and clean the nozzle\nG92 E0 ; Reset extruder to [0] zero end of cleaning run\nG1 E-1 F500 ; Retract filiment by 1 mm to reduce string effect\nG1 X180 F4000 ; quick wipe away from the filament line / purge\nM117 End Clean ; Indicate nozzle clean in progress on LCD\n;\nM117 Printing...\n; Begin printing with sliced GCode after here\n;"
},
"machine_end_gcode": {
"default_value": ";\n; end_gcode\nG92 E0 ; zero the extruded length\nG1 E-5 F9000 ; retract\nM104 S0 ; turn off temperature\nM140 S0 ; turn off bed\nG91 ; relative positioning\nG1 E-1 F300 ; retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+20 E-5 X-20 Y-20 F7200 ; move Z up a bit and retract filament even more\nG1 X320 Y150 F10000 ; move right mid\nM107 ; turn off layer fan\nM84 ; disable motors\nG90 ; absolute positioning\n;\n;EOF"
}
}
}

View file

@ -14,7 +14,7 @@
"platform_offset": [9, 0, 0],
"has_materials": false,
"has_machine_quality": true,
"exclude_materials": ["generic_hips", "generic_petg", "generic_bam", "generic_pva", "generic_tough_pla"],
"exclude_materials": ["generic_hips", "generic_petg", "generic_bam", "ultimaker_bam", "generic_pva", "ultimaker_pva", "generic_tough_pla", "ultimaker_tough_pla_black", "ultimaker_tough_pla_green", "ultimaker_tough_pla_red", "ultimaker_tough_pla_white"],
"first_start_actions": ["UM2UpgradeSelection"],
"supported_actions":["UM2UpgradeSelection", "UpgradeFirmware"]
},

View file

@ -11,7 +11,8 @@
"icon": "icon_ultimaker.png",
"platform": "ultimaker_platform.stl",
"has_materials": true,
"exclude_materials": ["generic_hips", "generic_petg", "generic_bam", "generic_pva", "generic_tough_pla"],
"has_machine_quality": true,
"exclude_materials": ["generic_hips", "generic_petg", "generic_bam", "ultimaker_bam", "generic_pva", "ultimaker_pva", "generic_tough_pla", "ultimaker_tough_pla_black", "ultimaker_tough_pla_green", "ultimaker_tough_pla_red", "ultimaker_tough_pla_white"],
"first_start_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel"],
"supported_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel", "UpgradeFirmware"]
},

View file

@ -11,7 +11,9 @@
"icon": "icon_ultimaker.png",
"platform": "ultimaker_platform.stl",
"has_materials": true,
"exclude_materials": ["generic_hips", "generic_petg", "generic_bam", "generic_pva", "generic_tough_pla"],
"has_machine_quality": true,
"quality_definition": "ultimaker_original",
"exclude_materials": ["generic_hips", "generic_petg", "generic_bam", "ultimaker_bam", "generic_pva", "ultimaker_pva", "generic_tough_pla", "ultimaker_tough_pla_black", "ultimaker_tough_pla_green", "ultimaker_tough_pla_red", "ultimaker_tough_pla_white"],
"machine_extruder_trains":
{
"0": "ultimaker_original_dual_1st",

View file

@ -10,6 +10,7 @@
"icon": "icon_ultimaker.png",
"platform": "ultimaker2_platform.obj",
"platform_texture": "UltimakerPlusbackplate.png",
"quality_definition": "ultimaker_original",
"first_start_actions": ["UMOCheckup", "BedLevel"],
"supported_actions": ["UMOCheckup", "BedLevel", "UpgradeFirmware"]
},

View file

@ -0,0 +1,42 @@
{
"id": "uni_print_3d",
"name": "UNI-PRINT-3D",
"version": 2,
"inherits": "fdmprinter",
"metadata":
{
"visible": true,
"author": "Alexander Rössler",
"category": "Other",
"manufacturer": "TheCoolTool",
"file_formats": "text/x-ngc;text/x-gcode",
"platform": "uni_print_3d_platform.stl",
"platform_offset": [0, 0, 0]
},
"overrides": {
"machine_name": { "default_value": "UNI-PRINT-3D" },
"machine_heated_bed": { "default_value": true },
"machine_width": { "default_value": 186 },
"machine_height": { "default_value": 230 },
"machine_depth": { "default_value": 220 },
"machine_center_is_zero": { "default_value": true },
"machine_nozzle_size": { "default_value": 0.4 },
"material_diameter": { "default_value": 1.75 },
"machine_nozzle_heat_up_speed": { "default_value": 2.0 },
"machine_nozzle_cool_down_speed": { "default_value": 2.0 },
"machine_head_shape_min_x": { "default_value": 75 },
"machine_head_shape_min_y": { "default_value": 18 },
"machine_head_shape_max_x": { "default_value": 18 },
"machine_head_shape_max_y": { "default_value": 35 },
"machine_nozzle_gantry_distance": { "default_value": 55 },
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_start_gcode": {
"default_value": "M53; enable feed-hold\nG0 Z2.0; always start from the same height to compensate backlash\nG28; move extruder to 0\nM420 R0.0 E0.0 D0.0 P0.1 ; turn the lights on\nM107; turn off fan\nG64 P0.05 Q0.05; path blending settings\nG23; unretract"
},
"machine_end_gcode": {
"default_value": "M104 P0 ; turn off hotend\nG0 X-80 Y100; move the extruder out of the way\nM420 R0.0 E0.1 D0.0 P0.6 ; signalize end of print\nM140 P0 ; turn off heatbed\nM65 P16 ; turn off external fan\nM65 P15 ; turn off power"
}
}
}

View file

@ -0,0 +1,52 @@
{
"version": 2,
"name": "Wanhao Duplicator 4S",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Ricardo Snoek",
"manufacturer": "Wanhao",
"file_formats": "text/x-gcode",
"icon": "wanhao-icon.png",
"has_materials": true,
"platform": "wanhao_225_145_platform.obj",
"platform_texture": "Wanhaobackplate.png",
"platform_offset": [
0,
-28,
5
]
},
"overrides": {
"machine_name": {
"default_value": "Wanhao Duplicator 4S"
},
"machine_width": {
"default_value": 225
},
"machine_height": {
"default_value": 150
},
"machine_depth": {
"default_value": 150
},
"machine_heated_bed": {
"default_value": true
},
"machine_nozzle_size": {
"default_value": 0.4
},
"machine_gcode_flavor": {
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
"default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..."
},
"machine_end_gcode": {
"default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
},
"material_diameter": {
"default_value": 1.75
}
}
}

View file

@ -0,0 +1,58 @@
{
"version": 2,
"name": "Wanhao Duplicator 6",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Ricardo Snoek",
"manufacturer": "Wanhao",
"file_formats": "text/x-gcode",
"icon": "wanhao-icon.png",
"has_materials": true,
"platform": "wanhao_200_200_platform.obj",
"platform_texture": "Wanhaobackplate.png",
"platform_offset": [
0,
-28,
0
],
"supported_actions": [
"UpgradeFirmware"
]
},
"overrides": {
"machine_name": {
"default_value": "Wanhao Duplicator 6"
},
"machine_width": {
"default_value": 200
},
"machine_height": {
"default_value": 180
},
"machine_depth": {
"default_value": 200
},
"machine_nozzle_size": {
"default_value": 0.4
},
"machine_heated_bed": {
"default_value": true
},
"gantry_height": {
"default_value": 55
},
"machine_gcode_flavor": {
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
"default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..."
},
"machine_end_gcode": {
"default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
},
"material_diameter": {
"default_value": 1.75
}
}
}

View file

@ -0,0 +1,52 @@
{
"version": 2,
"name": "Wanhao Duplicator 6 Plus",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Ricardo Snoek",
"manufacturer": "Wanhao",
"file_formats": "text/x-gcode",
"icon": "wanhao-icon.png",
"has_materials": true,
"platform": "wanhao_200_200_platform.obj",
"platform_texture": "Wanhaobackplate.png",
"platform_offset": [
0,
-28,
0
]
},
"overrides": {
"machine_name": {
"default_value": "Wanhao Duplicator 6 Plus"
},
"machine_width": {
"default_value": 200
},
"machine_height": {
"default_value": 180
},
"machine_depth": {
"default_value": 200
},
"machine_nozzle_size": {
"default_value": 0.4
},
"machine_heated_bed": {
"default_value": true
},
"machine_gcode_flavor": {
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
"default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..."
},
"machine_end_gcode": {
"default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
},
"material_diameter": {
"default_value": 1.75
}
}
}

View file

@ -0,0 +1,55 @@
{
"version": 2,
"name": "Wanhao Duplicator 5S",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Ricardo Snoek",
"manufacturer": "Wanhao",
"file_formats": "text/x-gcode",
"icon": "wanhao-icon.png",
"has_materials": true,
"platform": "wanhao_300_200_platform.obj",
"platform_texture": "Wanhaobackplate.png",
"platform_offset": [
0,
-28,
0
]
},
"overrides": {
"machine_name": {
"default_value": "Wanhao Duplicator 5S"
},
"machine_width": {
"default_value": 300
},
"machine_height": {
"default_value": 600
},
"machine_depth": {
"default_value": 200
},
"machine_center_is_zero": {
"default_value": false
},
"machine_heated_bed": {
"default_value": false
},
"machine_nozzle_size": {
"default_value": 0.4
},
"machine_gcode_flavor": {
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
"default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..."
},
"machine_end_gcode": {
"default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
},
"material_diameter": {
"default_value": 3
}
}
}

View file

@ -0,0 +1,52 @@
{
"version": 2,
"name": "Wanhao Duplicator 5S Mini",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Ricardo Snoek",
"manufacturer": "Wanhao",
"file_formats": "text/x-gcode",
"icon": "wanhao-icon.png",
"has_materials": true,
"platform": "wanhao_300_200_platform.obj",
"platform_texture": "Wanhaobackplate.png",
"platform_offset": [
0,
-28,
0
]
},
"overrides": {
"machine_name": {
"default_value": "Wanhao Duplicator 5S Mini"
},
"machine_width": {
"default_value": 300
},
"machine_height": {
"default_value": 180
},
"machine_depth": {
"default_value": 200
},
"machine_heated_bed": {
"default_value": false
},
"machine_nozzle_size": {
"default_value": 0.4
},
"machine_gcode_flavor": {
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
"default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..."
},
"machine_end_gcode": {
"default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
},
"material_diameter": {
"default_value": 3
}
}
}

View file

@ -0,0 +1,52 @@
{
"version": 2,
"name": "Wanhao Duplicator i3",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Ricardo Snoek",
"manufacturer": "Wanhao",
"file_formats": "text/x-gcode",
"icon": "wanhao-icon.png",
"has_materials": true,
"platform": "wanhao_200_200_platform.obj",
"platform_texture": "Wanhaobackplate.png",
"platform_offset": [
0,
-28,
0
]
},
"overrides": {
"machine_name": {
"default_value": "Wanhao Duplicator i3"
},
"machine_width": {
"default_value": 200
},
"machine_height": {
"default_value": 180
},
"machine_depth": {
"default_value": 200
},
"machine_heated_bed": {
"default_value": true
},
"machine_nozzle_size": {
"default_value": 0.4
},
"machine_gcode_flavor": {
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
"default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..."
},
"machine_end_gcode": {
"default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
},
"material_diameter": {
"default_value": 1.75
}
}
}

View file

@ -0,0 +1,52 @@
{
"version": 2,
"name": "Wanhao Duplicator i3 Mini",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Ricardo Snoek",
"manufacturer": "Wanhao",
"file_formats": "text/x-gcode",
"icon": "wanhao-icon.png",
"has_materials": true,
"platform": "wanhao_110_110_platform.obj",
"platform_texture": "Wanhaobackplate.png",
"platform_offset": [
0,
-15,
7
]
},
"overrides": {
"machine_name": {
"default_value": "Wanhao Duplicator i3 Mini"
},
"machine_width": {
"default_value": 110
},
"machine_height": {
"default_value": 110
},
"machine_depth": {
"default_value": 110
},
"machine_heated_bed": {
"default_value": false
},
"machine_nozzle_size": {
"default_value": 0.4
},
"machine_gcode_flavor": {
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
"default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..."
},
"machine_end_gcode": {
"default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
},
"material_diameter": {
"default_value": 1.75
}
}
}

View file

@ -0,0 +1,52 @@
{
"version": 2,
"name": "Wanhao Duplicator i3 Plus",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Ricardo Snoek",
"manufacturer": "Wanhao",
"file_formats": "text/x-gcode",
"icon": "wanhao-icon.png",
"has_materials": true,
"platform": "wanhao_200_200_platform.obj",
"platform_texture": "Wanhaobackplate.png",
"platform_offset": [
0,
-28,
0
]
},
"overrides": {
"machine_name": {
"default_value": "Wanhao Duplicator i3 Plus"
},
"machine_width": {
"default_value": 200
},
"machine_depth": {
"default_value": 200
},
"machine_height": {
"default_value": 180
},
"machine_heated_bed": {
"default_value": true
},
"machine_nozzle_size": {
"default_value": 0.4
},
"machine_gcode_flavor": {
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
"default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..."
},
"machine_end_gcode": {
"default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
},
"material_diameter": {
"default_value": 1.75
}
}
}

View file

@ -43,7 +43,7 @@ msgstr "G-Code-Datei"
#: /home/ruben/Projects/Cura/plugins/ModelChecker/ModelChecker.py:30
msgctxt "@info:title"
msgid "3D Model Assistant"
msgstr ""
msgstr "3D-Modell-Assistent"
#: /home/ruben/Projects/Cura/plugins/ModelChecker/ModelChecker.py:80
#, python-brace-format
@ -53,7 +53,7 @@ msgid ""
"<p>{model_names}</p>\n"
"<p>Find out how to ensure the best possible print quality and reliability.</p>\n"
"<p><a href=\"https://ultimaker.com/3D-model-assistant\">View print quality guide</a></p>"
msgstr ""
msgstr "<p>Ein oder mehrere 3D-Modelle können möglicherweise aufgrund der Modellgröße und Materialkonfiguration nicht optimal gedruckt werden:</p>\n<p>{model_names}</p>\n<p>Erfahren Sie, wie Sie die bestmögliche Druckqualität und Zuverlässigkeit sicherstellen.</p>\n<p><a href=“https://ultimaker.com/3D-model-assistant“>Leitfaden zu Druckqualität anzeigen</a></p>"
#: /home/ruben/Projects/Cura/plugins/Doodle3D-cura-plugin/Doodle3D/D3DCloudPrintOutputDevicePlugin.py:65
msgctxt "@action:button"
@ -155,17 +155,17 @@ msgstr "Über USB verbunden"
#: /home/ruben/Projects/Cura/plugins/X3GWriter/__init__.py:15
msgctxt "X3G Writer File Description"
msgid "X3G File"
msgstr "X3D-Datei"
msgstr "X3G-Datei"
#: /home/ruben/Projects/Cura/plugins/X3GWriter/build/GPX-prefix/src/GPX/slicerplugins/cura15.06/X3gWriter/__init__.py:16
msgctxt "X3g Writer Plugin Description"
msgid "Writes X3g to files"
msgstr ""
msgstr "Schreibt X3g in Dateien"
#: /home/ruben/Projects/Cura/plugins/X3GWriter/build/GPX-prefix/src/GPX/slicerplugins/cura15.06/X3gWriter/__init__.py:21
msgctxt "X3g Writer File Description"
msgid "X3g File"
msgstr ""
msgstr "X3g-Datei"
#: /home/ruben/Projects/Cura/plugins/GCodeGzWriter/__init__.py:17
#: /home/ruben/Projects/Cura/plugins/GCodeGzReader/__init__.py:17
@ -186,13 +186,13 @@ msgstr "Vorbereiten"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:23
msgctxt "@action:button Preceded by 'Ready to'."
msgid "Save to Removable Drive"
msgstr "Speichern auf Datenträger"
msgstr "Speichern auf Wechseldatenträger"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:24
#, python-brace-format
msgctxt "@item:inlistbox"
msgid "Save to Removable Drive {0}"
msgstr "Auf Datenträger speichern {0}"
msgstr "Auf Wechseldatenträger speichern {0}"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:64
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py:113
@ -204,7 +204,7 @@ msgstr "Es sind keine Dateiformate zum Schreiben vorhanden!"
#, python-brace-format
msgctxt "@info:progress Don't translate the XML tags <filename>!"
msgid "Saving to Removable Drive <filename>{0}</filename>"
msgstr "Wird auf Datenträger gespeichert <filename>{0}</filename>"
msgstr "Wird auf Wechseldatenträger gespeichert <filename>{0}</filename>"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:94
msgctxt "@info:title"
@ -229,7 +229,7 @@ msgstr "Bei dem Versuch, auf {device} zu schreiben, wurde ein Dateiname nicht ge
#, python-brace-format
msgctxt "@info:status"
msgid "Could not save to removable drive {0}: {1}"
msgstr "Konnte nicht auf dem Datenträger gespeichert werden {0}: {1}"
msgstr "Konnte nicht auf dem Wechseldatenträger gespeichert werden {0}: {1}"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:137
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:133
@ -243,7 +243,7 @@ msgstr "Fehler"
#, python-brace-format
msgctxt "@info:status"
msgid "Saved to Removable Drive {0} as {1}"
msgstr "Auf Datenträger {0} gespeichert als {1}"
msgstr "Auf Wechseldatenträger {0} gespeichert als {1}"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:145
msgctxt "@info:title"
@ -259,7 +259,7 @@ msgstr "Auswerfen"
#, python-brace-format
msgctxt "@action"
msgid "Eject removable device {0}"
msgstr "Datenträger auswerfen {0}"
msgstr "Wechseldatenträger auswerfen {0}"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:151
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:163
@ -289,7 +289,7 @@ msgstr "Auswurf fehlgeschlagen {0}. Möglicherweise wird das Laufwerk von einem
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/WindowsRemovableDrivePlugin.py:68
msgctxt "@item:intext"
msgid "Removable Drive"
msgstr "Datenträger"
msgstr "Wechseldatenträger"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/LegacyUM3OutputDevice.py:70
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py:78
@ -570,12 +570,12 @@ msgstr "Daten werden erfasst"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:49
msgctxt "@action:button"
msgid "More info"
msgstr ""
msgstr "Mehr Infos"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:50
msgctxt "@action:tooltip"
msgid "See more information on what data Cura sends."
msgstr ""
msgstr "Siehe mehr Informationen dazu, was Cura sendet."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:52
msgctxt "@action:button"
@ -602,9 +602,7 @@ msgctxt "@info:status"
msgid ""
"Could not export using \"{}\" quality!\n"
"Felt back to \"{}\"."
msgstr ""
"Exportieren in \"{}\" Qualität nicht möglich!\n"
"Zurückgeschaltet auf \"{}\"."
msgstr "Exportieren in \"{}\" Qualität nicht möglich!\nZurückgeschaltet auf \"{}\"."
#: /home/ruben/Projects/Cura/plugins/ImageReader/__init__.py:14
msgctxt "@item:inlistbox"
@ -1014,22 +1012,22 @@ msgstr "Produktabmessungen"
#: /home/ruben/Projects/Cura/cura/Backups/Backup.py:99
msgctxt "@info:backup_failed"
msgid "Could not create archive from user data directory: {}"
msgstr ""
msgstr "Konnte kein Archiv von Benutzer-Datenverzeichnis {} erstellen"
#: /home/ruben/Projects/Cura/cura/Backups/Backup.py:104
msgctxt "@info:title"
msgid "Backup"
msgstr ""
msgstr "Backup"
#: /home/ruben/Projects/Cura/cura/Backups/Backup.py:116
msgctxt "@info:backup_failed"
msgid "Tried to restore a Cura backup without having proper data or meta data."
msgstr ""
msgstr "Versucht, ein Cura-Backup-Verzeichnis ohne entsprechende Daten oder Metadaten wiederherzustellen."
#: /home/ruben/Projects/Cura/cura/Backups/Backup.py:126
msgctxt "@info:backup_failed"
msgid "Tried to restore a Cura backup that does not match your current version."
msgstr ""
msgstr "Versucht, ein Cura-Backup zu erstellen, das nicht Ihrer aktuellen Version entspricht."
#: /home/ruben/Projects/Cura/cura/MultiplyObjectsJob.py:27
msgctxt "@info:status"
@ -1080,12 +1078,7 @@ msgid ""
" <p>Backups can be found in the configuration folder.</p>\n"
" <p>Please send us this Crash Report to fix the problem.</p>\n"
" "
msgstr ""
"<p><b>Hoppla, bei Ultimaker Cura ist ein Problem aufgetreten.</p></b>\n"
" <p>Beim Start ist ein nicht behebbarer Fehler aufgetreten. Er wurde möglicherweise durch einige falsche Konfigurationsdateien verursacht. Wir empfehlen ein Backup und Reset Ihrer Konfiguration.</p>\n"
" <p>Backups sind im Konfigurationsordner abgelegt.</p>\n"
" <p>Senden Sie uns diesen Absturzbericht bitte, um das Problem zu beheben.</p>\n"
" "
msgstr "<p><b>Hoppla, bei Ultimaker Cura ist ein Problem aufgetreten.</p></b>\n <p>Beim Start ist ein nicht behebbarer Fehler aufgetreten. Er wurde möglicherweise durch einige falsche Konfigurationsdateien verursacht. Wir empfehlen ein Backup und Reset Ihrer Konfiguration.</p>\n <p>Backups sind im Konfigurationsordner abgelegt.</p>\n <p>Senden Sie uns diesen Absturzbericht bitte, um das Problem zu beheben.</p>\n "
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:102
msgctxt "@action:button"
@ -1118,10 +1111,7 @@ msgid ""
"<p><b>A fatal error has occurred in Cura. Please send us this Crash Report to fix the problem</p></b>\n"
" <p>Please use the \"Send report\" button to post a bug report automatically to our servers</p>\n"
" "
msgstr ""
"<p><b>Ein schwerer Fehler ist in Cura aufgetreten. Senden Sie uns diesen Absturzbericht, um das Problem zu beheben</p></b>\n"
" <p>Verwenden Sie bitte die Schaltfläche „Bericht senden“, um den Fehlerbericht automatisch an unsere Server zu senden</p>\n"
" "
msgstr "<p><b>Ein schwerer Fehler ist in Cura aufgetreten. Senden Sie uns diesen Absturzbericht, um das Problem zu beheben</p></b>\n <p>Verwenden Sie bitte die Schaltfläche „Bericht senden“, um den Fehlerbericht automatisch an unsere Server zu senden</p>\n "
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:177
msgctxt "@title:groupbox"
@ -1435,7 +1425,7 @@ msgstr "Installiert"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxErrorPage.qml:16
msgctxt "@info"
msgid "Could not connect to the Cura Package database. Please check your connection."
msgstr ""
msgstr "Verbindung zur Cura Paket-Datenbank konnte nicht hergestellt werden. Bitte überprüfen Sie Ihre Verbindung."
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml:35
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxHeader.qml:26
@ -1446,17 +1436,17 @@ msgstr "Plugins"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml:79
msgctxt "@label"
msgid "Version"
msgstr ""
msgstr "Version"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml:85
msgctxt "@label"
msgid "Last updated"
msgstr ""
msgstr "Zuletzt aktualisiert"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml:91
msgctxt "@label"
msgid "Author"
msgstr ""
msgstr "Autor"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml:109
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:269
@ -1474,53 +1464,53 @@ msgstr "Aktualisierung"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:31
msgctxt "@action:button"
msgid "Updating"
msgstr ""
msgstr "Aktualisierung wird durchgeführt"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml:46
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:32
msgctxt "@action:button"
msgid "Updated"
msgstr ""
msgstr "Aktualisiert"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/Toolbox.qml:13
msgctxt "@title"
msgid "Toolbox"
msgstr ""
msgstr "Toolbox"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxBackColumn.qml:25
msgctxt "@action:button"
msgid "Back"
msgstr ""
msgstr "Zurück"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxFooter.qml:17
msgctxt "@info"
msgid "You will need to restart Cura before changes in packages have effect."
msgstr ""
msgstr "Cura muss neu gestartet werden, um die Änderungen der Pakete zu übernehmen."
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxFooter.qml:32
msgctxt "@info:button"
msgid "Quit Cura"
msgstr ""
msgstr "Quit Cura"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxHeader.qml:54
msgctxt "@title:tab"
msgid "Installed"
msgstr ""
msgstr "Installiert"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:19
msgctxt "@label"
msgid "Will install upon restarting"
msgstr ""
msgstr "Installiert nach Neustart"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:51
msgctxt "@action:button"
msgid "Downgrade"
msgstr ""
msgstr "Downgraden"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:51
msgctxt "@action:button"
msgid "Uninstall"
msgstr ""
msgstr "Deinstallieren"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml:16
msgctxt "@title:window"
@ -1533,10 +1523,7 @@ msgid ""
"This plugin contains a license.\n"
"You need to accept this license to install this plugin.\n"
"Do you agree with the terms below?"
msgstr ""
"Dieses Plugin enthält eine Lizenz.\n"
"Sie müssen diese Lizenz akzeptieren, um das Plugin zu installieren.\n"
"Stimmen Sie den nachfolgenden Bedingungen zu?"
msgstr "Dieses Plugin enthält eine Lizenz.\nSie müssen diese Lizenz akzeptieren, um das Plugin zu installieren.\nStimmen Sie den nachfolgenden Bedingungen zu?"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml:54
msgctxt "@action:button"
@ -1551,22 +1538,22 @@ msgstr "Ablehnen"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml:17
msgctxt "@label"
msgid "Featured"
msgstr ""
msgstr "Unterstützter"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml:20
msgctxt "@label"
msgid "Compatibility"
msgstr ""
msgstr "Kompatibilität"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxLoadingPage.qml:16
msgctxt "@info"
msgid "Fetching packages..."
msgstr ""
msgstr "Pakete werden abgeholt..."
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml:87
msgctxt "@label"
msgid "Contact"
msgstr ""
msgstr "Kontakt"
#: /home/ruben/Projects/Cura/plugins/ModelChecker/ModelChecker.qml:22
msgctxt "@info:tooltip"
@ -1651,10 +1638,7 @@ msgid ""
"To print directly to your printer over the network, please make sure your printer is connected to the network using a network cable or by connecting your printer to your WIFI network. If you don't connect Cura with your printer, you can still use a USB drive to transfer g-code files to your printer.\n"
"\n"
"Select your printer from the list below:"
msgstr ""
"Um über das Netzwerk direkt auf Ihrem Drucker zu drucken, stellen Sie bitte sicher, dass der Drucker mit dem Netzwerkkabel verbunden ist oder verbinden Sie Ihren Drucker mit Ihrem WLAN-Netzwerk. Wenn Sie Cura nicht mit Ihrem Drucker verbinden, können Sie dennoch ein USB-Laufwerk für die Übertragung von G-Code-Dateien auf Ihren Drucker verwenden.\n"
"\n"
"Wählen Sie Ihren Drucker aus der folgenden Liste:"
msgstr "Um über das Netzwerk direkt auf Ihrem Drucker zu drucken, stellen Sie bitte sicher, dass der Drucker mit dem Netzwerkkabel verbunden ist oder verbinden Sie Ihren Drucker mit Ihrem WLAN-Netzwerk. Wenn Sie Cura nicht mit Ihrem Drucker verbinden, können Sie dennoch ein USB-Laufwerk für die Übertragung von G-Code-Dateien auf Ihren Drucker verwenden.\n\nWählen Sie Ihren Drucker aus der folgenden Liste:"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:96
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:42
@ -2015,22 +1999,22 @@ msgstr "Aktive Skripts Nachbearbeitung ändern"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/MoreInfoWindow.qml:16
msgctxt "@title:window"
msgid "More information on anonymous data collection"
msgstr ""
msgstr "Weitere Informationen zur anonymen Datenerfassung"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/MoreInfoWindow.qml:66
msgctxt "@text:window"
msgid "Cura sends anonymous data to Ultimaker in order to improve the print quality and user experience. Below is an example of all the data that is sent."
msgstr ""
msgstr "Cura sendet anonyme Daten an Ultimaker, um die Druckqualität und Benutzererfahrung zu steigern. Nachfolgend ist ein Beispiel aller Daten, die gesendet werden."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/MoreInfoWindow.qml:101
msgctxt "@text:window"
msgid "I don't want to send these data"
msgstr ""
msgstr "Ich möchte diese Daten nicht senden."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/MoreInfoWindow.qml:111
msgctxt "@text:window"
msgid "Allow sending these data to Ultimaker and help us improve Cura"
msgstr ""
msgstr "Ich erlaube das Senden dieser Daten an Ultimaker, um Cura zu verbessern"
#: /home/ruben/Projects/Cura/plugins/ImageReader/ConfigUI.qml:19
msgctxt "@title:window"
@ -2544,9 +2528,7 @@ msgctxt "@text:window"
msgid ""
"You have customized some profile settings.\n"
"Would you like to keep or discard those settings?"
msgstr ""
"Sie haben einige Profileinstellungen angepasst.\n"
"Möchten Sie diese Einstellungen übernehmen oder verwerfen?"
msgstr "Sie haben einige Profileinstellungen angepasst.\nMöchten Sie diese Einstellungen übernehmen oder verwerfen?"
#: /home/ruben/Projects/Cura/resources/qml/DiscardOrKeepProfileChangesDialog.qml:110
msgctxt "@title:column"
@ -2609,7 +2591,7 @@ msgstr "Änderung Durchmesser bestätigen"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MaterialView.qml:95
msgctxt "@label (%1 is a number)"
msgid "The new filament diameter is set to %1 mm, which is not compatible with the current extruder. Do you wish to continue?"
msgstr ""
msgstr "Der neue Filament-Durchmesser wurde auf %1 mm eingestellt, was nicht kompatibel mit dem aktuellen Extruder ist. Möchten Sie fortfahren?"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MaterialView.qml:128
msgctxt "@label"
@ -2880,12 +2862,12 @@ msgstr "Extrem kleine Modelle skalieren"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:507
msgctxt "@info:tooltip"
msgid "Should models be selected after they are loaded?"
msgstr ""
msgstr "Sollten Modelle gewählt werden, nachdem sie geladen wurden?"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:512
msgctxt "@option:check"
msgid "Select models when loaded"
msgstr ""
msgstr "Modelle wählen, nachdem sie geladen wurden"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:522
msgctxt "@info:tooltip"
@ -2970,7 +2952,7 @@ msgstr "(Anonyme) Druckinformationen senden"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:708
msgctxt "@action:button"
msgid "More information"
msgstr ""
msgstr "Mehr Informationen"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:726
msgctxt "@label"
@ -3246,9 +3228,7 @@ msgctxt "@info:credit"
msgid ""
"Cura is developed by Ultimaker B.V. in cooperation with the community.\n"
"Cura proudly uses the following open source projects:"
msgstr ""
"Cura wurde von Ultimaker B.V. in Zusammenarbeit mit der Community entwickelt.\n"
"Cura verwendet mit Stolz die folgenden Open Source-Projekte:"
msgstr "Cura wurde von Ultimaker B.V. in Zusammenarbeit mit der Community entwickelt.\nCura verwendet mit Stolz die folgenden Open Source-Projekte:"
#: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:118
msgctxt "@label"
@ -3361,10 +3341,7 @@ msgid ""
"Some setting/override values are different from the values stored in the profile.\n"
"\n"
"Click to open the profile manager."
msgstr ""
"Einige Einstellungs-/Überschreibungswerte unterscheiden sich von den im Profil gespeicherten Werten.\n"
"\n"
"Klicken Sie, um den Profilmanager zu öffnen."
msgstr "Einige Einstellungs-/Überschreibungswerte unterscheiden sich von den im Profil gespeicherten Werten.\n\nKlicken Sie, um den Profilmanager zu öffnen."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:199
msgctxt "@label:textbox"
@ -3405,12 +3382,12 @@ msgstr "Sichtbarkeit einstellen wird konfiguriert..."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:621
msgctxt "@action:inmenu"
msgid "Collapse All"
msgstr ""
msgstr "Alle verkleinern"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:626
msgctxt "@action:inmenu"
msgid "Expand All"
msgstr ""
msgstr "Alle vergrößern"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingCategory.qml:249
msgctxt "@label"
@ -3418,10 +3395,7 @@ msgid ""
"Some hidden settings use values different from their normal calculated value.\n"
"\n"
"Click to make these settings visible."
msgstr ""
"Einige ausgeblendete Einstellungen verwenden Werte, die von ihren normalen, berechneten Werten abweichen.\n"
"\n"
"Klicken Sie, um diese Einstellungen sichtbar zu machen."
msgstr "Einige ausgeblendete Einstellungen verwenden Werte, die von ihren normalen, berechneten Werten abweichen.\n\nKlicken Sie, um diese Einstellungen sichtbar zu machen."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:61
msgctxt "@label Header for list of settings."
@ -3449,10 +3423,7 @@ msgid ""
"This setting has a value that is different from the profile.\n"
"\n"
"Click to restore the value of the profile."
msgstr ""
"Diese Einstellung hat einen vom Profil abweichenden Wert.\n"
"\n"
"Klicken Sie, um den Wert des Profils wiederherzustellen."
msgstr "Diese Einstellung hat einen vom Profil abweichenden Wert.\n\nKlicken Sie, um den Wert des Profils wiederherzustellen."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:286
msgctxt "@label"
@ -3460,10 +3431,7 @@ msgid ""
"This setting is normally calculated, but it currently has an absolute value set.\n"
"\n"
"Click to restore the calculated value."
msgstr ""
"Diese Einstellung wird normalerweise berechnet; aktuell ist jedoch ein Absolutwert eingestellt.\n"
"\n"
"Klicken Sie, um den berechneten Wert wiederherzustellen."
msgstr "Diese Einstellung wird normalerweise berechnet; aktuell ist jedoch ein Absolutwert eingestellt.\n\nKlicken Sie, um den berechneten Wert wiederherzustellen."
#: /home/ruben/Projects/Cura/resources/qml/PrinterOutput/ManualPrinterControl.qml:129
msgctxt "@label"
@ -3607,7 +3575,7 @@ msgstr "&Druckplatte"
#: /home/ruben/Projects/Cura/resources/qml/Menus/SettingVisibilityPresetsMenu.qml:13
msgctxt "@action:inmenu"
msgid "Visible Settings"
msgstr "Sichtbare Einstellungen:"
msgstr "Sichtbare Einstellungen"
#: /home/ruben/Projects/Cura/resources/qml/Menus/SettingVisibilityPresetsMenu.qml:43
msgctxt "@action:inmenu"
@ -3651,12 +3619,12 @@ msgstr "Extruder"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ConfigurationMenu/SyncButton.qml:16
msgctxt "@label:extruder label"
msgid "Yes"
msgstr ""
msgstr "Ja"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ConfigurationMenu/SyncButton.qml:16
msgctxt "@label:extruder label"
msgid "No"
msgstr ""
msgstr "Nein"
#: /home/ruben/Projects/Cura/resources/qml/Menus/RecentFilesMenu.qml:13
msgctxt "@title:menu menubar:file"
@ -3673,14 +3641,12 @@ msgctxt "@label:listbox"
msgid ""
"Print Setup disabled\n"
"G-code files cannot be modified"
msgstr ""
"Druckeinrichtung deaktiviert\n"
"G-Code-Dateien können nicht geändert werden"
msgstr "Druckeinrichtung deaktiviert\nG-Code-Dateien können nicht geändert werden"
#: /home/ruben/Projects/Cura/resources/qml/PrepareSidebar.qml:341
msgctxt "@label Hours and minutes"
msgid "00h 00min"
msgstr "00 St. 00 M."
msgstr "00 Stunden 00 Minuten"
#: /home/ruben/Projects/Cura/resources/qml/PrepareSidebar.qml:359
msgctxt "@tooltip"
@ -3954,7 +3920,7 @@ msgstr "Konfigurationsordner anzeigen"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:433
msgctxt "@action:menu"
msgid "Browse packages..."
msgstr ""
msgstr "Pakete durchsuchen..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:440
msgctxt "@action:inmenu menubar:view"
@ -4112,7 +4078,7 @@ msgstr "Er&weiterungen"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:274
msgctxt "@title:menu menubar:toplevel"
msgid "&Toolbox"
msgstr ""
msgstr "&Toolbox"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:281
msgctxt "@title:menu menubar:toplevel"
@ -4127,7 +4093,7 @@ msgstr "&Hilfe"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:335
msgctxt "@label"
msgid "This package will be installed after restarting."
msgstr ""
msgstr "Dieses Paket wird nach einem Neustart installiert."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:364
msgctxt "@action:button"
@ -4152,7 +4118,7 @@ msgstr "Möchten Sie wirklich ein neues Projekt beginnen? Damit werden das Druck
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:814
msgctxt "@window:title"
msgid "Install Package"
msgstr ""
msgstr "Paket installieren"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:821
msgctxt "@title:window"
@ -4319,7 +4285,7 @@ msgstr "Engine-Protokoll"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:70
msgctxt "@label"
msgid "Printer type"
msgstr "Druckertyp:"
msgstr "Druckertyp"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:372
msgctxt "@label"
@ -4360,7 +4326,7 @@ msgstr "An aktueller Druckplatte ausrichten"
#: MachineSettingsAction/plugin.json
msgctxt "description"
msgid "Provides a way to change machine settings (such as build volume, nozzle size, etc.)."
msgstr ""
msgstr "Beschreibt die Durchführung der Geräteeinstellung (z. B. Druckabmessung, Düsengröße usw.)"
#: MachineSettingsAction/plugin.json
msgctxt "name"
@ -4370,12 +4336,12 @@ msgstr "Beschreibung Geräteeinstellungen"
#: Toolbox/plugin.json
msgctxt "description"
msgid "Find, manage and install new Cura packages."
msgstr ""
msgstr "Neue Cura Pakete finden, verwalten und installieren."
#: Toolbox/plugin.json
msgctxt "name"
msgid "Toolbox"
msgstr ""
msgstr "Toolbox"
#: XRayView/plugin.json
msgctxt "description"
@ -4470,7 +4436,7 @@ msgstr "USB-Drucken"
#: UserAgreement/plugin.json
msgctxt "description"
msgid "Ask the user once if he/she agrees with our license."
msgstr ""
msgstr "Den Benutzer einmalig fragen, ob er unsere Lizenz akzeptiert."
#: UserAgreement/plugin.json
msgctxt "name"
@ -4480,12 +4446,12 @@ msgstr "UserAgreement"
#: X3GWriter/plugin.json
msgctxt "description"
msgid "Allows saving the resulting slice as an X3G file, to support printers that read this format (Malyan, Makerbot and other Sailfish-based printers)."
msgstr ""
msgstr "Ermöglicht das Speichern des resultierenden Slices als X3G-Datei, um Drucker zu unterstützen, die dieses Format lesen (Malyan, Makerbot und andere Sailfish-basierte Drucker)."
#: X3GWriter/plugin.json
msgctxt "name"
msgid "X3GWriter"
msgstr ""
msgstr "X3G-Writer"
#: GCodeGzWriter/plugin.json
msgctxt "description"
@ -4530,17 +4496,17 @@ msgstr "Live-Scripting-Tool"
#: RemovableDriveOutputDevice/plugin.json
msgctxt "description"
msgid "Provides removable drive hotplugging and writing support."
msgstr "Ermöglicht Hotplugging des Datenträgers und Beschreiben."
msgstr "Ermöglicht Hotplugging des Wechseldatenträgers und Beschreiben."
#: RemovableDriveOutputDevice/plugin.json
msgctxt "name"
msgid "Removable Drive Output Device Plugin"
msgstr "Ausgabegerät-Plugin für Datenträger"
msgstr "Ausgabegerät-Plugin für Wechseldatenträger"
#: UM3NetworkPrinting/plugin.json
msgctxt "description"
msgid "Manages network connections to Ultimaker 3 printers."
msgstr ""
msgstr "Verwaltet Netzwerkverbindungen zu Ultimaker 3-Druckern"
#: UM3NetworkPrinting/plugin.json
msgctxt "name"
@ -4670,12 +4636,12 @@ msgstr "Upgrade von Version 3.2 auf 3.3"
#: VersionUpgrade/VersionUpgrade33to34/plugin.json
msgctxt "description"
msgid "Upgrades configurations from Cura 3.3 to Cura 3.4."
msgstr ""
msgstr "Aktualisiert Konfigurationen von Cura 3.3 auf Cura 3.4."
#: VersionUpgrade/VersionUpgrade33to34/plugin.json
msgctxt "name"
msgid "Version Upgrade 3.3 to 3.4"
msgstr ""
msgstr "Upgrade von Version 3.3 auf 3.4"
#: VersionUpgrade/VersionUpgrade25to26/plugin.json
msgctxt "description"
@ -4820,7 +4786,7 @@ msgstr "3MF-Writer"
#: UltimakerMachineActions/plugin.json
msgctxt "description"
msgid "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.)."
msgstr ""
msgstr "Ermöglicht Maschinenabläufe für Ultimaker-Maschinen (z. B. Assistent für Bettnivellierung, Auswahl von Upgrades usw.)"
#: UltimakerMachineActions/plugin.json
msgctxt "name"

View file

@ -56,9 +56,7 @@ msgctxt "machine_start_gcode description"
msgid ""
"G-code commands to be executed at the very start - separated by \n"
"."
msgstr ""
"G-Code-Befehle, die zu Beginn ausgeführt werden sollen getrennt durch \n"
"."
msgstr "G-Code-Befehle, die zu Beginn ausgeführt werden sollen getrennt durch \n."
#: fdmprinter.def.json
msgctxt "machine_end_gcode label"
@ -70,9 +68,7 @@ msgctxt "machine_end_gcode description"
msgid ""
"G-code commands to be executed at the very end - separated by \n"
"."
msgstr ""
"G-Code-Befehle, die am Ende ausgeführt werden sollen getrennt durch \n"
"."
msgstr "G-Code-Befehle, die am Ende ausgeführt werden sollen getrennt durch \n."
#: fdmprinter.def.json
msgctxt "material_guid label"
@ -1087,7 +1083,7 @@ msgstr "Reihenfolge des Wanddrucks optimieren"
#: fdmprinter.def.json
msgctxt "optimize_wall_printing_order description"
msgid "Optimize the order in which walls are printed so as to reduce the number of retractions and the distance travelled. Most parts will benefit from this being enabled but some may actually take longer so please compare the print time estimates with and without optimization. First layer is not optimized when choosing brim as build plate adhesion type."
msgstr ""
msgstr "Optimieren Sie die Reihenfolge, in der die Wände gedruckt werden, um die Anzahl der Einzüge und die zurückgelegten Distanzen zu reduzieren. Dieser Schritt bringt für die meisten Teile Vorteile, allerdings werden einige möglicherweise länger benötigen. Vergleichen Sie deshalb bitte die Schätzung der Druckzeiten mit und ohne Optimierung. Bei Wahl eines Brims als Druckplattenhaftungstyp ist die erste Schicht nicht optimiert."
#: fdmprinter.def.json
msgctxt "outer_inset_first label"
@ -1677,22 +1673,22 @@ msgstr "Keine Füllungsbereiche generieren, die kleiner als dieser sind (stattde
#: fdmprinter.def.json
msgctxt "infill_support_enabled label"
msgid "Infill Support"
msgstr ""
msgstr "Füllstruktur"
#: fdmprinter.def.json
msgctxt "infill_support_enabled description"
msgid "Print infill structures only where tops of the model should be supported. Enabling this reduces print time and material usage, but leads to ununiform object strength."
msgstr ""
msgstr "Drucken Sie Füllstrukturen nur dort, wo das Modell gestützt werden soll. Die Aktivierung dieser Option reduziert die Druckdauer und den Materialverbrauch, führt jedoch zu einer ungleichmäßigen Objektdicke."
#: fdmprinter.def.json
msgctxt "infill_support_angle label"
msgid "Infill Overhang Angle"
msgstr ""
msgstr "Füllung für Überhänge Stützstruktur"
#: fdmprinter.def.json
msgctxt "infill_support_angle description"
msgid "The minimum angle of internal overhangs for which infill is added. At a value of 0° objects are totally filled with infill, 90° will not provide any infill."
msgstr ""
msgstr "Der Mindestwinkel für Überhänge, für welche eine Stützstruktur zugefügt wird. Bei einem Wert von 0° werden Objekte komplett gefüllt, bei 90° wird keine Füllung ausgeführt."
#: fdmprinter.def.json
msgctxt "skin_preshrink label"
@ -2037,12 +2033,12 @@ msgstr "Das Fenster, in dem die maximale Anzahl von Einzügen durchgeführt wird
#: fdmprinter.def.json
msgctxt "limit_support_retractions label"
msgid "Limit Support Retractions"
msgstr ""
msgstr "Stützstruktur-Einzüge einschränken"
#: fdmprinter.def.json
msgctxt "limit_support_retractions description"
msgid "Omit retraction when moving from support to support in a straight line. Enabling this setting saves print time, but can lead to excesive stringing within the support structure."
msgstr ""
msgstr "Lassen Sie den Einzug beim Vorgehen von Stützstruktur zu Stützstruktur in einer geraden Linie aus. Die Aktivierung dieser Einstellung spart Druckzeit, kann jedoch zu übermäßigem Fadenziehen innerhalb der Stützstruktur führen."
#: fdmprinter.def.json
msgctxt "material_standby_temperature label"
@ -2737,17 +2733,17 @@ msgstr "Alle"
#: fdmprinter.def.json
msgctxt "retraction_combing option noskin"
msgid "Not in Skin"
msgstr ""
msgstr "Nicht in Außenhaut"
#: fdmprinter.def.json
msgctxt "retraction_combing_max_distance label"
msgid "Max Comb Distance With No Retract"
msgstr ""
msgstr "Max. Kammentfernung ohne Einziehen"
#: fdmprinter.def.json
msgctxt "retraction_combing_max_distance description"
msgid "When non-zero, combing travel moves that are longer than this distance will use retraction."
msgstr ""
msgstr "Bei Nicht-Null verwenden die Combing-Fahrbewegungen, die länger als die Distanz sind, die Einziehfunktion."
#: fdmprinter.def.json
msgctxt "travel_retract_before_outer_wall label"
@ -2772,12 +2768,12 @@ msgstr "Die Düse vermeidet bei der Bewegung bereits gedruckte Teile. Diese Opti
#: fdmprinter.def.json
msgctxt "travel_avoid_supports label"
msgid "Avoid Supports When Traveling"
msgstr ""
msgstr "Stützstrukturen bei Bewegung umgehen"
#: fdmprinter.def.json
msgctxt "travel_avoid_supports description"
msgid "The nozzle avoids already printed supports when traveling. This option is only available when combing is enabled."
msgstr ""
msgstr "Die Düse vermeidet bei der Bewegung bereits gedruckte Stützstrukturen. Diese Option ist nur verfügbar, wenn Combing aktiviert ist."
#: fdmprinter.def.json
msgctxt "travel_avoid_distance label"
@ -3137,12 +3133,12 @@ msgstr "Quer"
#: fdmprinter.def.json
msgctxt "support_wall_count label"
msgid "Support Wall Line Count"
msgstr ""
msgstr "Anzahl der Wandlinien der Stützstruktur"
#: fdmprinter.def.json
msgctxt "support_wall_count description"
msgid "The number of walls with which to surround support infill. Adding a wall can make support print more reliably and can support overhangs better, but increases print time and material used."
msgstr ""
msgstr "Die Anzahl der Wände, mit denen die Stützstruktur-Füllung umgeben wird. Das Hinzufügen einer Wand kann den Druck der Stützstruktur zuverlässiger machen und Überhänge besser unterstützen. Es erhöht jedoch die Druckzeit und den Materialverbrauch."
#: fdmprinter.def.json
msgctxt "zig_zaggify_support label"
@ -3714,9 +3710,7 @@ msgctxt "skirt_gap description"
msgid ""
"The horizontal distance between the skirt and the first layer of the print.\n"
"This is the minimum distance. Multiple skirt lines will extend outwards from this distance."
msgstr ""
"Der horizontale Abstand zwischen dem Skirt und der ersten Schicht des Drucks.\n"
"Es handelt sich dabei um den Mindestabstand. Ab diesem Abstand werden mehrere Skirt-Linien in äußerer Richtung angebracht."
msgstr "Der horizontale Abstand zwischen dem Skirt und der ersten Schicht des Drucks.\nEs handelt sich dabei um den Mindestabstand. Ab diesem Abstand werden mehrere Skirt-Linien in äußerer Richtung angebracht."
#: fdmprinter.def.json
msgctxt "skirt_brim_minimal_length label"
@ -4676,12 +4670,12 @@ msgstr "Die Mindestgröße eines Linienabschnitts nach dem Slicen. Wenn Sie dies
#: fdmprinter.def.json
msgctxt "meshfix_maximum_travel_resolution label"
msgid "Maximum Travel Resolution"
msgstr ""
msgstr "Maximale Bewegungsauflösung"
#: fdmprinter.def.json
msgctxt "meshfix_maximum_travel_resolution description"
msgid "The minimum size of a travel line segment after slicing. If you increase this, the travel moves will have less smooth corners. This may allow the printer to keep up with the speed it has to process g-code, but it may cause model avoidance to become less accurate."
msgstr ""
msgstr "Die maximale Größe eines Bewegungsliniensegments nach dem Slicen. Wenn Sie diesen Wert erhöhen, weisen die Fahrtbewegungen weniger glatte Kanten aus. Das ermöglicht dem Drucker, die für die Verarbeitung eines G-Codes erforderliche Geschwindigkeit aufrechtzuerhalten, allerdings kann das Modell damit auch weniger akkurat werden."
#: fdmprinter.def.json
msgctxt "support_skip_some_zags label"
@ -4846,22 +4840,22 @@ msgstr "Die Größe der Taschen bei Überkreuzung im 3D-Quermuster bei Höhen, i
#: fdmprinter.def.json
msgctxt "cross_infill_density_image label"
msgid "Cross Infill Density Image"
msgstr ""
msgstr "Querfülldichte Bild"
#: fdmprinter.def.json
msgctxt "cross_infill_density_image description"
msgid "The file location of an image of which the brightness values determine the minimal density at the corresponding location in the infill of the print."
msgstr ""
msgstr "Die Dateiposition eines Bildes, von dem die Helligkeitswerte die minimale Dichte an der entsprechenden Position in der Füllung des Drucks bestimmen."
#: fdmprinter.def.json
msgctxt "cross_support_density_image label"
msgid "Cross Fill Density Image for Support"
msgstr ""
msgstr "Querfülldichte Bild für Stützstruktur"
#: fdmprinter.def.json
msgctxt "cross_support_density_image description"
msgid "The file location of an image of which the brightness values determine the minimal density at the corresponding location in the support."
msgstr ""
msgstr "Die Dateiposition eines Bildes, von dem die Helligkeitswerte die minimale Dichte an der entsprechenden Position in der Stützstruktur bestimmen."
#: fdmprinter.def.json
msgctxt "spaghetti_infill_enabled label"
@ -5173,9 +5167,7 @@ msgctxt "wireframe_up_half_speed description"
msgid ""
"Distance of an upward move which is extruded with half speed.\n"
"This can cause better adhesion to previous layers, while not heating the material in those layers too much. Only applies to Wire Printing."
msgstr ""
"Die Strecke einer Aufwärtsbewegung, die mit halber Geschwindigkeit extrudiert wird.\n"
"Dies kann zu einer besseren Haftung an vorhergehenden Schichten führen, während gleichzeitig ein Überhitzen des Materials in diesen Schichten vermieden wird. Dies gilt nur für das Drucken mit Drahtstruktur."
msgstr "Die Strecke einer Aufwärtsbewegung, die mit halber Geschwindigkeit extrudiert wird.\nDies kann zu einer besseren Haftung an vorhergehenden Schichten führen, während gleichzeitig ein Überhitzen des Materials in diesen Schichten vermieden wird. Dies gilt nur für das Drucken mit Drahtstruktur."
#: fdmprinter.def.json
msgctxt "wireframe_top_jump label"
@ -5300,7 +5292,7 @@ msgstr "Maximale Abweichung für Anpassschichten"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation description"
msgid "The maximum allowed height different from the base layer height."
msgstr ""
msgstr "Die max. zulässige Höhendifferenz von der Basisschichthöhe."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step label"
@ -5535,7 +5527,7 @@ msgstr "Diese Einstellungen werden nur verwendet, wenn CuraEngine nicht seitens
#: fdmprinter.def.json
msgctxt "center_object label"
msgid "Center Object"
msgstr ""
msgstr "Objekt zentrieren"
#: fdmprinter.def.json
msgctxt "center_object description"
@ -5545,7 +5537,7 @@ msgstr "Ermöglicht das Zentrieren des Objekts in der Mitte eines Druckbetts (0,
#: fdmprinter.def.json
msgctxt "mesh_position_x label"
msgid "Mesh Position X"
msgstr ""
msgstr "Netzposition X"
#: fdmprinter.def.json
msgctxt "mesh_position_x description"
@ -5555,7 +5547,7 @@ msgstr "Verwendeter Versatz für das Objekt in X-Richtung."
#: fdmprinter.def.json
msgctxt "mesh_position_y label"
msgid "Mesh Position Y"
msgstr ""
msgstr "Netzposition Y"
#: fdmprinter.def.json
msgctxt "mesh_position_y description"
@ -5565,7 +5557,7 @@ msgstr "Verwendeter Versatz für das Objekt in Y-Richtung."
#: fdmprinter.def.json
msgctxt "mesh_position_z label"
msgid "Mesh Position Z"
msgstr ""
msgstr "Netzposition Z"
#: fdmprinter.def.json
msgctxt "mesh_position_z description"

View file

@ -43,7 +43,7 @@ msgstr "Archivo GCode"
#: /home/ruben/Projects/Cura/plugins/ModelChecker/ModelChecker.py:30
msgctxt "@info:title"
msgid "3D Model Assistant"
msgstr ""
msgstr "Asistente del modelo 3D"
#: /home/ruben/Projects/Cura/plugins/ModelChecker/ModelChecker.py:80
#, python-brace-format
@ -53,7 +53,10 @@ msgid ""
"<p>{model_names}</p>\n"
"<p>Find out how to ensure the best possible print quality and reliability.</p>\n"
"<p><a href=\"https://ultimaker.com/3D-model-assistant\">View print quality guide</a></p>"
msgstr ""
msgstr "<p>Es posible que uno o más modelos 3D no se impriman correctamente debido al tamaño del modelo y la configuración del material:</p>\n"
"<p>{model_names}</p>\n"
"<p>Obtenga más información sobre cómo garantizar la mejor calidad y fiabilidad de impresión posible.</p>\n"
"<p><a href=\"https://ultimaker.com/3D-model-assistant\">Ver guía de impresión de calidad</a></p>"
#: /home/ruben/Projects/Cura/plugins/Doodle3D-cura-plugin/Doodle3D/D3DCloudPrintOutputDevicePlugin.py:65
msgctxt "@action:button"
@ -160,12 +163,12 @@ msgstr "Archivo X3G"
#: /home/ruben/Projects/Cura/plugins/X3GWriter/build/GPX-prefix/src/GPX/slicerplugins/cura15.06/X3gWriter/__init__.py:16
msgctxt "X3g Writer Plugin Description"
msgid "Writes X3g to files"
msgstr ""
msgstr "Escribe X3g en archivos"
#: /home/ruben/Projects/Cura/plugins/X3GWriter/build/GPX-prefix/src/GPX/slicerplugins/cura15.06/X3gWriter/__init__.py:21
msgctxt "X3g Writer File Description"
msgid "X3g File"
msgstr ""
msgstr "Archivo X3g"
#: /home/ruben/Projects/Cura/plugins/GCodeGzWriter/__init__.py:17
#: /home/ruben/Projects/Cura/plugins/GCodeGzReader/__init__.py:17
@ -570,12 +573,12 @@ msgstr "Recopilando datos"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:49
msgctxt "@action:button"
msgid "More info"
msgstr ""
msgstr "Más información"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:50
msgctxt "@action:tooltip"
msgid "See more information on what data Cura sends."
msgstr ""
msgstr "Obtenga más información sobre qué datos envía Cura."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:52
msgctxt "@action:button"
@ -602,9 +605,8 @@ msgctxt "@info:status"
msgid ""
"Could not export using \"{}\" quality!\n"
"Felt back to \"{}\"."
msgstr ""
"No ha podido exportarse con la calidad \"{}\"\n"
"Retroceder a \"{}»."
msgstr "No ha podido exportarse con la calidad \"{}\"\n"
"Retroceder a \"{}\"."
#: /home/ruben/Projects/Cura/plugins/ImageReader/__init__.py:14
msgctxt "@item:inlistbox"
@ -1014,22 +1016,22 @@ msgstr "Volumen de impresión"
#: /home/ruben/Projects/Cura/cura/Backups/Backup.py:99
msgctxt "@info:backup_failed"
msgid "Could not create archive from user data directory: {}"
msgstr ""
msgstr "No se ha podido crear el archivo desde el directorio de datos de usuario: {}"
#: /home/ruben/Projects/Cura/cura/Backups/Backup.py:104
msgctxt "@info:title"
msgid "Backup"
msgstr ""
msgstr "Copia de seguridad"
#: /home/ruben/Projects/Cura/cura/Backups/Backup.py:116
msgctxt "@info:backup_failed"
msgid "Tried to restore a Cura backup without having proper data or meta data."
msgstr ""
msgstr "Se ha intentado restaurar una copia de seguridad de Cura sin tener los datos o metadatos adecuados."
#: /home/ruben/Projects/Cura/cura/Backups/Backup.py:126
msgctxt "@info:backup_failed"
msgid "Tried to restore a Cura backup that does not match your current version."
msgstr ""
msgstr "Se ha intentado restaurar una copia de seguridad de Cura que no coincide con la versión actual."
#: /home/ruben/Projects/Cura/cura/MultiplyObjectsJob.py:27
msgctxt "@info:status"
@ -1120,7 +1122,7 @@ msgid ""
" "
msgstr ""
"<p><b>Se ha producido un error grave en Cura. Envíenos este informe de errores para que podamos solucionar el problema.</p></b>\n"
" <p>Utilice el botón «Enviar informe» para publicar automáticamente el informe de errores en nuestros servidores.</p>\n"
" <p>Utilice el botón \"Enviar informe\" para publicar automáticamente el informe de errores en nuestros servidores.</p>\n"
" "
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:177
@ -1435,7 +1437,7 @@ msgstr "Instalado"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxErrorPage.qml:16
msgctxt "@info"
msgid "Could not connect to the Cura Package database. Please check your connection."
msgstr ""
msgstr "No se ha podido conectar con la base de datos del Paquete Cura. Compruebe la conexión."
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml:35
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxHeader.qml:26
@ -1446,17 +1448,17 @@ msgstr "Complementos"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml:79
msgctxt "@label"
msgid "Version"
msgstr ""
msgstr "Versión"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml:85
msgctxt "@label"
msgid "Last updated"
msgstr ""
msgstr "Última actualización"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml:91
msgctxt "@label"
msgid "Author"
msgstr ""
msgstr "Autor"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml:109
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:269
@ -1474,53 +1476,53 @@ msgstr "Actualizar"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:31
msgctxt "@action:button"
msgid "Updating"
msgstr ""
msgstr "Actualizando"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml:46
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:32
msgctxt "@action:button"
msgid "Updated"
msgstr ""
msgstr "Actualizado"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/Toolbox.qml:13
msgctxt "@title"
msgid "Toolbox"
msgstr ""
msgstr "Cuadro de herramientas"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxBackColumn.qml:25
msgctxt "@action:button"
msgid "Back"
msgstr ""
msgstr "Atrás"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxFooter.qml:17
msgctxt "@info"
msgid "You will need to restart Cura before changes in packages have effect."
msgstr ""
msgstr "Tendrá que reiniciar Cura para que los cambios de los paquetes surtan efecto."
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxFooter.qml:32
msgctxt "@info:button"
msgid "Quit Cura"
msgstr ""
msgstr "Salir de Cura"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxHeader.qml:54
msgctxt "@title:tab"
msgid "Installed"
msgstr ""
msgstr "Instalado"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:19
msgctxt "@label"
msgid "Will install upon restarting"
msgstr ""
msgstr "Se instalará después de reiniciar"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:51
msgctxt "@action:button"
msgid "Downgrade"
msgstr ""
msgstr "Degradar"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:51
msgctxt "@action:button"
msgid "Uninstall"
msgstr ""
msgstr "Desinstalar"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml:16
msgctxt "@title:window"
@ -1533,10 +1535,7 @@ msgid ""
"This plugin contains a license.\n"
"You need to accept this license to install this plugin.\n"
"Do you agree with the terms below?"
msgstr ""
"Este complemento incluye una licencia.\n"
"Debe aceptar dicha licencia para instalar el complemento.\n"
"¿Acepta las condiciones que aparecen a continuación?"
msgstr "Este complemento incluye una licencia.\nDebe aceptar dicha licencia para instalar el complemento.\n¿Acepta las condiciones que aparecen a continuación?"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml:54
msgctxt "@action:button"
@ -1551,22 +1550,22 @@ msgstr "Rechazar"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml:17
msgctxt "@label"
msgid "Featured"
msgstr ""
msgstr "Destacado"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml:20
msgctxt "@label"
msgid "Compatibility"
msgstr ""
msgstr "Compatibilidad"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxLoadingPage.qml:16
msgctxt "@info"
msgid "Fetching packages..."
msgstr ""
msgstr "Buscando paquetes..."
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml:87
msgctxt "@label"
msgid "Contact"
msgstr ""
msgstr "Contacto"
#: /home/ruben/Projects/Cura/plugins/ModelChecker/ModelChecker.qml:22
msgctxt "@info:tooltip"
@ -1651,10 +1650,7 @@ msgid ""
"To print directly to your printer over the network, please make sure your printer is connected to the network using a network cable or by connecting your printer to your WIFI network. If you don't connect Cura with your printer, you can still use a USB drive to transfer g-code files to your printer.\n"
"\n"
"Select your printer from the list below:"
msgstr ""
"Para imprimir directamente en la impresora a través de la red, asegúrese de que esta está conectada a la red utilizando un cable de red o conéctela a la red wifi. Si no conecta Cura con la impresora, también puede utilizar una unidad USB para transferir archivos GCode a la impresora.\n"
"\n"
"Seleccione la impresora de la siguiente lista:"
msgstr "Para imprimir directamente en la impresora a través de la red, asegúrese de que esta está conectada a la red utilizando un cable de red o conéctela a la red wifi. Si no conecta Cura con la impresora, también puede utilizar una unidad USB para transferir archivos GCode a la impresora.\n\nSeleccione la impresora de la siguiente lista:"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:96
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:42
@ -2015,22 +2011,22 @@ msgstr "Cambia las secuencias de comandos de posprocesamiento."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/MoreInfoWindow.qml:16
msgctxt "@title:window"
msgid "More information on anonymous data collection"
msgstr ""
msgstr "Más información sobre la recopilación de datos anónimos"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/MoreInfoWindow.qml:66
msgctxt "@text:window"
msgid "Cura sends anonymous data to Ultimaker in order to improve the print quality and user experience. Below is an example of all the data that is sent."
msgstr ""
msgstr "Cura envía datos anónimos a Ultimaker para mejorar la calidad de impresión y la experiencia de usuario. A continuación, hay un ejemplo de todos los datos que se han enviado."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/MoreInfoWindow.qml:101
msgctxt "@text:window"
msgid "I don't want to send these data"
msgstr ""
msgstr "No quiero enviar estos datos"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/MoreInfoWindow.qml:111
msgctxt "@text:window"
msgid "Allow sending these data to Ultimaker and help us improve Cura"
msgstr ""
msgstr "Permita enviar estos datos a Ultimaker y ayúdenos a mejorar Cura"
#: /home/ruben/Projects/Cura/plugins/ImageReader/ConfigUI.qml:19
msgctxt "@title:window"
@ -2544,9 +2540,7 @@ msgctxt "@text:window"
msgid ""
"You have customized some profile settings.\n"
"Would you like to keep or discard those settings?"
msgstr ""
"Ha personalizado parte de los ajustes del perfil.\n"
"¿Desea descartar los cambios o guardarlos?"
msgstr "Ha personalizado parte de los ajustes del perfil.\n¿Desea descartar los cambios o guardarlos?"
#: /home/ruben/Projects/Cura/resources/qml/DiscardOrKeepProfileChangesDialog.qml:110
msgctxt "@title:column"
@ -2609,7 +2603,7 @@ msgstr "Confirmar cambio de diámetro"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MaterialView.qml:95
msgctxt "@label (%1 is a number)"
msgid "The new filament diameter is set to %1 mm, which is not compatible with the current extruder. Do you wish to continue?"
msgstr ""
msgstr "El nuevo diámetro del filamento está ajustado en %1 mm y no es compatible con el extrusor actual. ¿Desea continuar?"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MaterialView.qml:128
msgctxt "@label"
@ -2880,12 +2874,12 @@ msgstr "Escalar modelos demasiado pequeños"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:507
msgctxt "@info:tooltip"
msgid "Should models be selected after they are loaded?"
msgstr ""
msgstr "¿Se deberían seleccionar los modelos después de haberse cargado?"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:512
msgctxt "@option:check"
msgid "Select models when loaded"
msgstr ""
msgstr "Seleccionar modelos al abrirlos"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:522
msgctxt "@info:tooltip"
@ -2970,7 +2964,7 @@ msgstr "Enviar información (anónima) de impresión"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:708
msgctxt "@action:button"
msgid "More information"
msgstr ""
msgstr "Más información"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:726
msgctxt "@label"
@ -3246,9 +3240,7 @@ msgctxt "@info:credit"
msgid ""
"Cura is developed by Ultimaker B.V. in cooperation with the community.\n"
"Cura proudly uses the following open source projects:"
msgstr ""
"Ultimaker B.V. ha desarrollado Cura en cooperación con la comunidad.\n"
"Cura se enorgullece de utilizar los siguientes proyectos de código abierto:"
msgstr "Ultimaker B.V. ha desarrollado Cura en cooperación con la comunidad.\nCura se enorgullece de utilizar los siguientes proyectos de código abierto:"
#: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:118
msgctxt "@label"
@ -3361,10 +3353,7 @@ msgid ""
"Some setting/override values are different from the values stored in the profile.\n"
"\n"
"Click to open the profile manager."
msgstr ""
"Algunos valores de los ajustes o sobrescrituras son distintos a los valores almacenados en el perfil.\n"
"\n"
"Haga clic para abrir el administrador de perfiles."
msgstr "Algunos valores de los ajustes o sobrescrituras son distintos a los valores almacenados en el perfil.\n\nHaga clic para abrir el administrador de perfiles."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:199
msgctxt "@label:textbox"
@ -3405,12 +3394,12 @@ msgstr "Configurar visibilidad de los ajustes..."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:621
msgctxt "@action:inmenu"
msgid "Collapse All"
msgstr ""
msgstr "Contraer todo"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:626
msgctxt "@action:inmenu"
msgid "Expand All"
msgstr ""
msgstr "Ampliar todo"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingCategory.qml:249
msgctxt "@label"
@ -3418,10 +3407,7 @@ msgid ""
"Some hidden settings use values different from their normal calculated value.\n"
"\n"
"Click to make these settings visible."
msgstr ""
"Algunos ajustes ocultos utilizan valores diferentes de los valores normales calculados.\n"
"\n"
"Haga clic para mostrar estos ajustes."
msgstr "Algunos ajustes ocultos utilizan valores diferentes de los valores normales calculados.\n\nHaga clic para mostrar estos ajustes."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:61
msgctxt "@label Header for list of settings."
@ -3449,10 +3435,7 @@ msgid ""
"This setting has a value that is different from the profile.\n"
"\n"
"Click to restore the value of the profile."
msgstr ""
"Este ajuste tiene un valor distinto del perfil.\n"
"\n"
"Haga clic para restaurar el valor del perfil."
msgstr "Este ajuste tiene un valor distinto del perfil.\n\nHaga clic para restaurar el valor del perfil."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:286
msgctxt "@label"
@ -3460,10 +3443,7 @@ msgid ""
"This setting is normally calculated, but it currently has an absolute value set.\n"
"\n"
"Click to restore the calculated value."
msgstr ""
"Este ajuste se calcula normalmente pero actualmente tiene un valor absoluto establecido.\n"
"\n"
"Haga clic para restaurar el valor calculado."
msgstr "Este ajuste se calcula normalmente pero actualmente tiene un valor absoluto establecido.\n\nHaga clic para restaurar el valor calculado."
#: /home/ruben/Projects/Cura/resources/qml/PrinterOutput/ManualPrinterControl.qml:129
msgctxt "@label"
@ -3607,7 +3587,7 @@ msgstr "&Placa de impresión"
#: /home/ruben/Projects/Cura/resources/qml/Menus/SettingVisibilityPresetsMenu.qml:13
msgctxt "@action:inmenu"
msgid "Visible Settings"
msgstr "Ajustes visibles:"
msgstr "Ajustes visibles"
#: /home/ruben/Projects/Cura/resources/qml/Menus/SettingVisibilityPresetsMenu.qml:43
msgctxt "@action:inmenu"
@ -3651,12 +3631,12 @@ msgstr "Extrusor"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ConfigurationMenu/SyncButton.qml:16
msgctxt "@label:extruder label"
msgid "Yes"
msgstr ""
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Menus/ConfigurationMenu/SyncButton.qml:16
msgctxt "@label:extruder label"
msgid "No"
msgstr ""
msgstr "No"
#: /home/ruben/Projects/Cura/resources/qml/Menus/RecentFilesMenu.qml:13
msgctxt "@title:menu menubar:file"
@ -3673,9 +3653,7 @@ msgctxt "@label:listbox"
msgid ""
"Print Setup disabled\n"
"G-code files cannot be modified"
msgstr ""
"Ajustes de impresión deshabilitados\n"
"No se pueden modificar los archivos GCode"
msgstr "Ajustes de impresión deshabilitados\nNo se pueden modificar los archivos GCode"
#: /home/ruben/Projects/Cura/resources/qml/PrepareSidebar.qml:341
msgctxt "@label Hours and minutes"
@ -3954,7 +3932,7 @@ msgstr "Mostrar carpeta de configuración"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:433
msgctxt "@action:menu"
msgid "Browse packages..."
msgstr ""
msgstr "Examinar paquetes..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:440
msgctxt "@action:inmenu menubar:view"
@ -4112,7 +4090,7 @@ msgstr "E&xtensiones"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:274
msgctxt "@title:menu menubar:toplevel"
msgid "&Toolbox"
msgstr ""
msgstr "&Cuadro de herramientas"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:281
msgctxt "@title:menu menubar:toplevel"
@ -4127,7 +4105,7 @@ msgstr "A&yuda"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:335
msgctxt "@label"
msgid "This package will be installed after restarting."
msgstr ""
msgstr "Este paquete se instalará después de reiniciar."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:364
msgctxt "@action:button"
@ -4152,7 +4130,7 @@ msgstr "¿Está seguro de que desea iniciar un nuevo proyecto? Esto borrará la
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:814
msgctxt "@window:title"
msgid "Install Package"
msgstr ""
msgstr "Instalar paquete"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:821
msgctxt "@title:window"
@ -4319,7 +4297,7 @@ msgstr "Registro del motor"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:70
msgctxt "@label"
msgid "Printer type"
msgstr "Tipo de impresora:"
msgstr "Tipo de impresora"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:372
msgctxt "@label"
@ -4360,7 +4338,7 @@ msgstr "Organizar placa de impresión actual"
#: MachineSettingsAction/plugin.json
msgctxt "description"
msgid "Provides a way to change machine settings (such as build volume, nozzle size, etc.)."
msgstr ""
msgstr "Permite cambiar los ajustes de la máquina (como el volumen de impresión, el tamaño de la tobera, etc.)."
#: MachineSettingsAction/plugin.json
msgctxt "name"
@ -4370,12 +4348,12 @@ msgstr "Acción Ajustes de la máquina"
#: Toolbox/plugin.json
msgctxt "description"
msgid "Find, manage and install new Cura packages."
msgstr ""
msgstr "Buscar, administrar e instalar nuevos paquetes de Cura."
#: Toolbox/plugin.json
msgctxt "name"
msgid "Toolbox"
msgstr ""
msgstr "Cuadro de herramientas"
#: XRayView/plugin.json
msgctxt "description"
@ -4470,7 +4448,7 @@ msgstr "Impresión USB"
#: UserAgreement/plugin.json
msgctxt "description"
msgid "Ask the user once if he/she agrees with our license."
msgstr ""
msgstr "Preguntar al usuario una vez si acepta la licencia"
#: UserAgreement/plugin.json
msgctxt "name"
@ -4480,12 +4458,12 @@ msgstr "UserAgreement"
#: X3GWriter/plugin.json
msgctxt "description"
msgid "Allows saving the resulting slice as an X3G file, to support printers that read this format (Malyan, Makerbot and other Sailfish-based printers)."
msgstr ""
msgstr "Permite guardar el segmento resultante como un archivo X3G para dar compatibilidad a impresoras que leen este formato (Malyan, Makerbot y otras impresoras basadas en Sailfish)."
#: X3GWriter/plugin.json
msgctxt "name"
msgid "X3GWriter"
msgstr ""
msgstr "X3GWriter"
#: GCodeGzWriter/plugin.json
msgctxt "description"
@ -4540,7 +4518,7 @@ msgstr "Complemento de dispositivo de salida de unidad extraíble"
#: UM3NetworkPrinting/plugin.json
msgctxt "description"
msgid "Manages network connections to Ultimaker 3 printers."
msgstr ""
msgstr "Gestiona las conexiones de red a las impresoras Ultimaker 3."
#: UM3NetworkPrinting/plugin.json
msgctxt "name"
@ -4670,12 +4648,12 @@ msgstr "Actualización de la versión 3.2 a la 3.3"
#: VersionUpgrade/VersionUpgrade33to34/plugin.json
msgctxt "description"
msgid "Upgrades configurations from Cura 3.3 to Cura 3.4."
msgstr ""
msgstr "Actualiza la configuración de Cura 3.3 a Cura 3.4."
#: VersionUpgrade/VersionUpgrade33to34/plugin.json
msgctxt "name"
msgid "Version Upgrade 3.3 to 3.4"
msgstr ""
msgstr "Actualización de la versión 3.3 a la 3.4"
#: VersionUpgrade/VersionUpgrade25to26/plugin.json
msgctxt "description"
@ -4820,7 +4798,7 @@ msgstr "Escritor de 3MF"
#: UltimakerMachineActions/plugin.json
msgctxt "description"
msgid "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.)."
msgstr ""
msgstr "Proporciona las acciones de la máquina de las máquinas Ultimaker (como un asistente para la nivelación de la plataforma, la selección de actualizaciones, etc.)."
#: UltimakerMachineActions/plugin.json
msgctxt "name"

View file

@ -79,7 +79,7 @@ msgstr "Coordenada Y del desplazamiento de la tobera."
#: fdmextruder.def.json
msgctxt "machine_extruder_start_code label"
msgid "Extruder Start G-Code"
msgstr "Gcode inicial del extrusor"
msgstr "GCode inicial del extrusor"
#: fdmextruder.def.json
msgctxt "machine_extruder_start_code description"
@ -119,7 +119,7 @@ msgstr "Coordenada Y de la posición de inicio cuando se enciende el extrusor."
#: fdmextruder.def.json
msgctxt "machine_extruder_end_code label"
msgid "Extruder End G-Code"
msgstr "Gcode final del extrusor"
msgstr "GCode final del extrusor"
#: fdmextruder.def.json
msgctxt "machine_extruder_end_code description"

View file

@ -57,9 +57,7 @@ msgctxt "machine_start_gcode description"
msgid ""
"G-code commands to be executed at the very start - separated by \n"
"."
msgstr ""
"Los comandos de GCode que se ejecutarán justo al inicio separados por - \n"
"."
msgstr "Los comandos de GCode que se ejecutarán justo al inicio separados por - \n."
#: fdmprinter.def.json
msgctxt "machine_end_gcode label"
@ -71,9 +69,7 @@ msgctxt "machine_end_gcode description"
msgid ""
"G-code commands to be executed at the very end - separated by \n"
"."
msgstr ""
"Los comandos de GCode que se ejecutarán justo al final separados por -\n"
"."
msgstr "Los comandos de GCode que se ejecutarán justo al final separados por -\n."
#: fdmprinter.def.json
msgctxt "material_guid label"
@ -433,7 +429,7 @@ msgstr "Silueta 2D del cabezal de impresión (incluidas las tapas del ventilador
#: fdmprinter.def.json
msgctxt "gantry_height label"
msgid "Gantry height"
msgstr "Altura del caballete"
msgstr "Altura del puente"
#: fdmprinter.def.json
msgctxt "gantry_height description"
@ -1088,7 +1084,7 @@ msgstr "Optimizar el orden de impresión de paredes"
#: fdmprinter.def.json
msgctxt "optimize_wall_printing_order description"
msgid "Optimize the order in which walls are printed so as to reduce the number of retractions and the distance travelled. Most parts will benefit from this being enabled but some may actually take longer so please compare the print time estimates with and without optimization. First layer is not optimized when choosing brim as build plate adhesion type."
msgstr ""
msgstr "Optimizar el orden en el que se imprimen las paredes a fin de reducir el número de retracciones y la distancia recorrida. La mayoría de los componentes se beneficiarán si este ajuste está habilitado pero, en algunos casos, se puede tardar más, por lo que deben compararse las previsiones de tiempo de impresión con y sin optimización. La primera capa no está optimizada al elegir el borde como el tipo de adhesión de la placa de impresión."
#: fdmprinter.def.json
msgctxt "outer_inset_first label"
@ -1678,22 +1674,22 @@ msgstr "No genere áreas con un relleno inferior a este (utilice forro)."
#: fdmprinter.def.json
msgctxt "infill_support_enabled label"
msgid "Infill Support"
msgstr ""
msgstr "Soporte de relleno"
#: fdmprinter.def.json
msgctxt "infill_support_enabled description"
msgid "Print infill structures only where tops of the model should be supported. Enabling this reduces print time and material usage, but leads to ununiform object strength."
msgstr ""
msgstr "Imprimir estructuras de relleno solo cuando se deban soportar las partes superiores del modelo. Habilitar esto reduce el tiempo de impresión y el uso de material, pero ocasiona que la resistencia del objeto no sea uniforme."
#: fdmprinter.def.json
msgctxt "infill_support_angle label"
msgid "Infill Overhang Angle"
msgstr ""
msgstr "Ángulo de voladizo de relleno"
#: fdmprinter.def.json
msgctxt "infill_support_angle description"
msgid "The minimum angle of internal overhangs for which infill is added. At a value of 0° objects are totally filled with infill, 90° will not provide any infill."
msgstr ""
msgstr "El ángulo mínimo de los voladizos internos para los que se agrega relleno. A partir de un valor de 0 º todos los objetos estarán totalmente rellenos, a 90 º no se proporcionará ningún relleno."
#: fdmprinter.def.json
msgctxt "skin_preshrink label"
@ -2038,12 +2034,12 @@ msgstr "Ventana en la que se aplica el recuento máximo de retracciones. Este va
#: fdmprinter.def.json
msgctxt "limit_support_retractions label"
msgid "Limit Support Retractions"
msgstr ""
msgstr "Limitar las retracciones de soporte"
#: fdmprinter.def.json
msgctxt "limit_support_retractions description"
msgid "Omit retraction when moving from support to support in a straight line. Enabling this setting saves print time, but can lead to excesive stringing within the support structure."
msgstr ""
msgstr "Omitir la retracción al moverse de soporte a soporte en línea recta. Habilitar este ajuste ahorra tiempo de impresión pero puede ocasionar un encordado excesivo en la estructura de soporte."
#: fdmprinter.def.json
msgctxt "material_standby_temperature label"
@ -2738,17 +2734,17 @@ msgstr "Todo"
#: fdmprinter.def.json
msgctxt "retraction_combing option noskin"
msgid "Not in Skin"
msgstr ""
msgstr "No en el forro"
#: fdmprinter.def.json
msgctxt "retraction_combing_max_distance label"
msgid "Max Comb Distance With No Retract"
msgstr ""
msgstr "Distancia de peinada máxima sin retracción"
#: fdmprinter.def.json
msgctxt "retraction_combing_max_distance description"
msgid "When non-zero, combing travel moves that are longer than this distance will use retraction."
msgstr ""
msgstr "Si no es cero, los movimientos de desplazamiento de peinada que sean superiores a esta distancia utilizarán retracción."
#: fdmprinter.def.json
msgctxt "travel_retract_before_outer_wall label"
@ -2773,12 +2769,12 @@ msgstr "La tobera evita las partes ya impresas al desplazarse. Esta opción solo
#: fdmprinter.def.json
msgctxt "travel_avoid_supports label"
msgid "Avoid Supports When Traveling"
msgstr ""
msgstr "Evitar soportes al desplazarse"
#: fdmprinter.def.json
msgctxt "travel_avoid_supports description"
msgid "The nozzle avoids already printed supports when traveling. This option is only available when combing is enabled."
msgstr ""
msgstr "La tobera evita los soportes ya impresos al desplazarse. Esta opción solo está disponible cuando se ha activado la opción de peinada."
#: fdmprinter.def.json
msgctxt "travel_avoid_distance label"
@ -3078,7 +3074,7 @@ msgstr "Tocando la placa de impresión"
#: fdmprinter.def.json
msgctxt "support_type option everywhere"
msgid "Everywhere"
msgstr "En todas partes"
msgstr "En todos sitios"
#: fdmprinter.def.json
msgctxt "support_angle label"
@ -3138,12 +3134,12 @@ msgstr "Cruz"
#: fdmprinter.def.json
msgctxt "support_wall_count label"
msgid "Support Wall Line Count"
msgstr ""
msgstr "Recuento de líneas de pared del soporte"
#: fdmprinter.def.json
msgctxt "support_wall_count description"
msgid "The number of walls with which to surround support infill. Adding a wall can make support print more reliably and can support overhangs better, but increases print time and material used."
msgstr ""
msgstr "El número de paredes con las que el soporte rodea el relleno. Añadir una pared puede hacer que la impresión de soporte sea más fiable y pueda soportar mejor los voladizos pero aumenta el tiempo de impresión y el material utilizado."
#: fdmprinter.def.json
msgctxt "zig_zaggify_support label"
@ -3715,9 +3711,7 @@ msgctxt "skirt_gap description"
msgid ""
"The horizontal distance between the skirt and the first layer of the print.\n"
"This is the minimum distance. Multiple skirt lines will extend outwards from this distance."
msgstr ""
"La distancia horizontal entre la falda y la primera capa de la impresión.\n"
"Se trata de la distancia mínima. Múltiples líneas de falda se extenderán hacia el exterior a partir de esta distancia."
msgstr "La distancia horizontal entre la falda y la primera capa de la impresión.\nSe trata de la distancia mínima. Múltiples líneas de falda se extenderán hacia el exterior a partir de esta distancia."
#: fdmprinter.def.json
msgctxt "skirt_brim_minimal_length label"
@ -4677,12 +4671,12 @@ msgstr "El tamaño mínimo de un segmento de línea tras la segmentación. Si se
#: fdmprinter.def.json
msgctxt "meshfix_maximum_travel_resolution label"
msgid "Maximum Travel Resolution"
msgstr ""
msgstr "Resolución de desplazamiento máximo"
#: fdmprinter.def.json
msgctxt "meshfix_maximum_travel_resolution description"
msgid "The minimum size of a travel line segment after slicing. If you increase this, the travel moves will have less smooth corners. This may allow the printer to keep up with the speed it has to process g-code, but it may cause model avoidance to become less accurate."
msgstr ""
msgstr "El tamaño mínimo de un segmento de línea de desplazamiento tras la segmentación. Si se aumenta, los movimientos de desplazamiento tendrán esquinas menos suavizadas. Esto puede le permite a la impresora mantener la velocidad que necesita para procesar GCode pero puede ocasionar que evitar el modelo sea menos preciso."
#: fdmprinter.def.json
msgctxt "support_skip_some_zags label"
@ -4847,22 +4841,22 @@ msgstr "Tamaño de las bolsas en cruces del patrón de cruz 3D en las alturas en
#: fdmprinter.def.json
msgctxt "cross_infill_density_image label"
msgid "Cross Infill Density Image"
msgstr ""
msgstr "Imagen de densidad de relleno cruzada"
#: fdmprinter.def.json
msgctxt "cross_infill_density_image description"
msgid "The file location of an image of which the brightness values determine the minimal density at the corresponding location in the infill of the print."
msgstr ""
msgstr "La ubicación del archivo de una imagen de la que los valores de brillo determinan la densidad mínima en la ubicación correspondiente en el relleno de la impresión."
#: fdmprinter.def.json
msgctxt "cross_support_density_image label"
msgid "Cross Fill Density Image for Support"
msgstr ""
msgstr "Imagen de densidad de relleno cruzada para soporte"
#: fdmprinter.def.json
msgctxt "cross_support_density_image description"
msgid "The file location of an image of which the brightness values determine the minimal density at the corresponding location in the support."
msgstr ""
msgstr "La ubicación del archivo de una imagen de la que los valores de brillo determinan la densidad mínima en la ubicación correspondiente del soporte."
#: fdmprinter.def.json
msgctxt "spaghetti_infill_enabled label"
@ -5174,9 +5168,7 @@ msgctxt "wireframe_up_half_speed description"
msgid ""
"Distance of an upward move which is extruded with half speed.\n"
"This can cause better adhesion to previous layers, while not heating the material in those layers too much. Only applies to Wire Printing."
msgstr ""
"Distancia de un movimiento ascendente que se extrude a media velocidad.\n"
"Esto puede causar una mejor adherencia a las capas anteriores, aunque no calienta demasiado el material en esas capas. Solo se aplica a la impresión de alambre."
msgstr "Distancia de un movimiento ascendente que se extrude a media velocidad.\nEsto puede causar una mejor adherencia a las capas anteriores, aunque no calienta demasiado el material en esas capas. Solo se aplica a la impresión de alambre."
#: fdmprinter.def.json
msgctxt "wireframe_top_jump label"
@ -5301,7 +5293,7 @@ msgstr "Variación máxima de las capas de adaptación"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation description"
msgid "The maximum allowed height different from the base layer height."
msgstr ""
msgstr "La diferencia de altura máxima permitida en comparación con la altura de la capa base."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step label"
@ -5366,7 +5358,7 @@ msgstr "Ancho máximo permitido de la cámara de aire por debajo de una línea d
#: fdmprinter.def.json
msgctxt "bridge_wall_coast label"
msgid "Bridge Wall Coasting"
msgstr "Deslizamiento por la pared del puente"
msgstr "Depósito por inercia de la pared del puente"
#: fdmprinter.def.json
msgctxt "bridge_wall_coast description"
@ -5536,7 +5528,7 @@ msgstr "Ajustes que únicamente se utilizan si CuraEngine no se ejecuta desde la
#: fdmprinter.def.json
msgctxt "center_object label"
msgid "Center Object"
msgstr ""
msgstr "Centrar objeto"
#: fdmprinter.def.json
msgctxt "center_object description"
@ -5546,7 +5538,7 @@ msgstr "Centrar o no el objeto en el centro de la plataforma de impresión (0, 0
#: fdmprinter.def.json
msgctxt "mesh_position_x label"
msgid "Mesh Position X"
msgstr ""
msgstr "Posición X en la malla"
#: fdmprinter.def.json
msgctxt "mesh_position_x description"
@ -5556,7 +5548,7 @@ msgstr "Desplazamiento aplicado al objeto en la dirección x."
#: fdmprinter.def.json
msgctxt "mesh_position_y label"
msgid "Mesh Position Y"
msgstr ""
msgstr "Posición Y en la malla"
#: fdmprinter.def.json
msgctxt "mesh_position_y description"
@ -5566,7 +5558,7 @@ msgstr "Desplazamiento aplicado al objeto en la dirección y."
#: fdmprinter.def.json
msgctxt "mesh_position_z label"
msgid "Mesh Position Z"
msgstr ""
msgstr "Posición Z en la malla"
#: fdmprinter.def.json
msgctxt "mesh_position_z description"

View file

@ -43,7 +43,7 @@ msgstr "Fichier GCode"
#: /home/ruben/Projects/Cura/plugins/ModelChecker/ModelChecker.py:30
msgctxt "@info:title"
msgid "3D Model Assistant"
msgstr ""
msgstr "Assistant de modèle 3D"
#: /home/ruben/Projects/Cura/plugins/ModelChecker/ModelChecker.py:80
#, python-brace-format
@ -53,7 +53,7 @@ msgid ""
"<p>{model_names}</p>\n"
"<p>Find out how to ensure the best possible print quality and reliability.</p>\n"
"<p><a href=\"https://ultimaker.com/3D-model-assistant\">View print quality guide</a></p>"
msgstr ""
msgstr "<p>Un ou plusieurs modèles 3D peuvent ne pas s'imprimer de manière optimale en raison de la taille du modèle et de la configuration matérielle :</p>\n<p>{model_names}</p>\n<p>Découvrez comment optimiser la qualité et la fiabilité de l'impression.</p>\n<p><a href=\"https://ultimaker.com/3D-model-assistant »>Consultez le guide de qualité d'impression</a></p>"
#: /home/ruben/Projects/Cura/plugins/Doodle3D-cura-plugin/Doodle3D/D3DCloudPrintOutputDevicePlugin.py:65
msgctxt "@action:button"
@ -160,12 +160,12 @@ msgstr "Fichier X3G"
#: /home/ruben/Projects/Cura/plugins/X3GWriter/build/GPX-prefix/src/GPX/slicerplugins/cura15.06/X3gWriter/__init__.py:16
msgctxt "X3g Writer Plugin Description"
msgid "Writes X3g to files"
msgstr ""
msgstr "Écrit X3G dans des fichiers"
#: /home/ruben/Projects/Cura/plugins/X3GWriter/build/GPX-prefix/src/GPX/slicerplugins/cura15.06/X3gWriter/__init__.py:21
msgctxt "X3g Writer File Description"
msgid "X3g File"
msgstr ""
msgstr "Fichier X3G"
#: /home/ruben/Projects/Cura/plugins/GCodeGzWriter/__init__.py:17
#: /home/ruben/Projects/Cura/plugins/GCodeGzReader/__init__.py:17
@ -570,12 +570,12 @@ msgstr "Collecte des données..."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:49
msgctxt "@action:button"
msgid "More info"
msgstr ""
msgstr "Plus d'informations"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:50
msgctxt "@action:tooltip"
msgid "See more information on what data Cura sends."
msgstr ""
msgstr "Voir plus d'informations sur les données envoyées par Cura."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:52
msgctxt "@action:button"
@ -602,9 +602,7 @@ msgctxt "@info:status"
msgid ""
"Could not export using \"{}\" quality!\n"
"Felt back to \"{}\"."
msgstr ""
"Impossible d'exporter avec la qualité \"{}\" !\n"
"Qualité redéfinie sur \"{}\"."
msgstr "Impossible d'exporter avec la qualité \"{}\" !\nQualité redéfinie sur \"{}\"."
#: /home/ruben/Projects/Cura/plugins/ImageReader/__init__.py:14
msgctxt "@item:inlistbox"
@ -813,7 +811,7 @@ msgstr "Support"
#: /home/ruben/Projects/Cura/cura/PrintInformation.py:105
msgctxt "@tooltip"
msgid "Skirt"
msgstr "Contour"
msgstr "Jupe"
#: /home/ruben/Projects/Cura/cura/PrintInformation.py:106
msgctxt "@tooltip"
@ -1014,22 +1012,22 @@ msgstr "Volume d'impression"
#: /home/ruben/Projects/Cura/cura/Backups/Backup.py:99
msgctxt "@info:backup_failed"
msgid "Could not create archive from user data directory: {}"
msgstr ""
msgstr "Impossible de créer une archive à partir du répertoire de données de l'utilisateur : {}"
#: /home/ruben/Projects/Cura/cura/Backups/Backup.py:104
msgctxt "@info:title"
msgid "Backup"
msgstr ""
msgstr "Sauvegarde"
#: /home/ruben/Projects/Cura/cura/Backups/Backup.py:116
msgctxt "@info:backup_failed"
msgid "Tried to restore a Cura backup without having proper data or meta data."
msgstr ""
msgstr "A essayé de restaurer une sauvegarde Cura sans disposer de données ou de métadonnées appropriées."
#: /home/ruben/Projects/Cura/cura/Backups/Backup.py:126
msgctxt "@info:backup_failed"
msgid "Tried to restore a Cura backup that does not match your current version."
msgstr ""
msgstr "A essayé de restaurer une sauvegarde Cura qui ne correspond pas à votre version actuelle."
#: /home/ruben/Projects/Cura/cura/MultiplyObjectsJob.py:27
msgctxt "@info:status"
@ -1080,12 +1078,7 @@ msgid ""
" <p>Backups can be found in the configuration folder.</p>\n"
" <p>Please send us this Crash Report to fix the problem.</p>\n"
" "
msgstr ""
"<p><b>Oups, un problème est survenu dans Ultimaker Cura.</p></b>\n"
" <p>Une erreur irrécupérable est survenue lors du démarrage. Elle peut avoir été causée par des fichiers de configuration incorrects. Nous vous suggérons de sauvegarder et de réinitialiser votre configuration.</p>\n"
" <p>Les sauvegardes se trouvent dans le dossier de configuration.</p>\n"
" <p>Veuillez nous envoyer ce rapport d'incident pour que nous puissions résoudre le problème.</p>\n"
" "
msgstr "<p><b>Oups, un problème est survenu dans Ultimaker Cura.</p></b>\n <p>Une erreur irrécupérable est survenue lors du démarrage. Elle peut avoir été causée par des fichiers de configuration incorrects. Nous vous suggérons de sauvegarder et de réinitialiser votre configuration.</p>\n <p>Les sauvegardes se trouvent dans le dossier de configuration.</p>\n <p>Veuillez nous envoyer ce rapport d'incident pour que nous puissions résoudre le problème.</p>\n "
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:102
msgctxt "@action:button"
@ -1118,10 +1111,7 @@ msgid ""
"<p><b>A fatal error has occurred in Cura. Please send us this Crash Report to fix the problem</p></b>\n"
" <p>Please use the \"Send report\" button to post a bug report automatically to our servers</p>\n"
" "
msgstr ""
"<p><b>Une erreur fatale est survenue dans Cura. Veuillez nous envoyer ce rapport d'incident pour résoudre le problème</p></b>\n"
" <p>Veuillez utiliser le bouton « Envoyer rapport » pour publier automatiquement un rapport d'erreur sur nos serveurs</p>\n"
" "
msgstr "<p><b>Une erreur fatale est survenue dans Cura. Veuillez nous envoyer ce rapport d'incident pour résoudre le problème</p></b>\n <p>Veuillez utiliser le bouton « Envoyer rapport » pour publier automatiquement un rapport d'erreur sur nos serveurs</p>\n "
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:177
msgctxt "@title:groupbox"
@ -1435,7 +1425,7 @@ msgstr "Installé"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxErrorPage.qml:16
msgctxt "@info"
msgid "Could not connect to the Cura Package database. Please check your connection."
msgstr ""
msgstr "Impossible de se connecter à la base de données Cura Package. Veuillez vérifier votre connexion."
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml:35
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxHeader.qml:26
@ -1446,17 +1436,17 @@ msgstr "Plug-ins"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml:79
msgctxt "@label"
msgid "Version"
msgstr ""
msgstr "Version"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml:85
msgctxt "@label"
msgid "Last updated"
msgstr ""
msgstr "Dernière mise à jour"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml:91
msgctxt "@label"
msgid "Author"
msgstr ""
msgstr "Auteur"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml:109
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:269
@ -1474,53 +1464,53 @@ msgstr "Mise à jour"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:31
msgctxt "@action:button"
msgid "Updating"
msgstr ""
msgstr "Mise à jour"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml:46
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:32
msgctxt "@action:button"
msgid "Updated"
msgstr ""
msgstr "Mis à jour"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/Toolbox.qml:13
msgctxt "@title"
msgid "Toolbox"
msgstr ""
msgstr "Boîte à outils"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxBackColumn.qml:25
msgctxt "@action:button"
msgid "Back"
msgstr ""
msgstr "Précédent"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxFooter.qml:17
msgctxt "@info"
msgid "You will need to restart Cura before changes in packages have effect."
msgstr ""
msgstr "Vous devez redémarrer Cura pour que les changements apportés aux paquets ne prennent effet."
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxFooter.qml:32
msgctxt "@info:button"
msgid "Quit Cura"
msgstr ""
msgstr "Quitter Cura"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxHeader.qml:54
msgctxt "@title:tab"
msgid "Installed"
msgstr ""
msgstr "Installé"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:19
msgctxt "@label"
msgid "Will install upon restarting"
msgstr ""
msgstr "S'installera au redémarrage"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:51
msgctxt "@action:button"
msgid "Downgrade"
msgstr ""
msgstr "Revenir à une version précédente"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:51
msgctxt "@action:button"
msgid "Uninstall"
msgstr ""
msgstr "Désinstaller"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml:16
msgctxt "@title:window"
@ -1533,10 +1523,7 @@ msgid ""
"This plugin contains a license.\n"
"You need to accept this license to install this plugin.\n"
"Do you agree with the terms below?"
msgstr ""
"Ce plug-in contient une licence.\n"
"Vous devez approuver cette licence pour installer ce plug-in.\n"
"Acceptez-vous les clauses ci-dessous ?"
msgstr "Ce plug-in contient une licence.\nVous devez approuver cette licence pour installer ce plug-in.\nAcceptez-vous les clauses ci-dessous ?"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml:54
msgctxt "@action:button"
@ -1551,22 +1538,22 @@ msgstr "Refuser"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml:17
msgctxt "@label"
msgid "Featured"
msgstr ""
msgstr "Fonctionnalités"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml:20
msgctxt "@label"
msgid "Compatibility"
msgstr ""
msgstr "Compatibilité"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxLoadingPage.qml:16
msgctxt "@info"
msgid "Fetching packages..."
msgstr ""
msgstr "Récupération des paquets..."
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml:87
msgctxt "@label"
msgid "Contact"
msgstr ""
msgstr "Contact"
#: /home/ruben/Projects/Cura/plugins/ModelChecker/ModelChecker.qml:22
msgctxt "@info:tooltip"
@ -1651,10 +1638,7 @@ msgid ""
"To print directly to your printer over the network, please make sure your printer is connected to the network using a network cable or by connecting your printer to your WIFI network. If you don't connect Cura with your printer, you can still use a USB drive to transfer g-code files to your printer.\n"
"\n"
"Select your printer from the list below:"
msgstr ""
"Pour imprimer directement sur votre imprimante sur le réseau, assurez-vous que votre imprimante est connectée au réseau via un câble réseau ou en connectant votre imprimante à votre réseau Wi-Fi. Si vous ne connectez pas Cura avec votre imprimante, vous pouvez utiliser une clé USB pour transférer les fichiers g-code sur votre imprimante.\n"
"\n"
"Sélectionnez votre imprimante dans la liste ci-dessous :"
msgstr "Pour imprimer directement sur votre imprimante sur le réseau, assurez-vous que votre imprimante est connectée au réseau via un câble réseau ou en connectant votre imprimante à votre réseau Wi-Fi. Si vous ne connectez pas Cura avec votre imprimante, vous pouvez utiliser une clé USB pour transférer les fichiers g-code sur votre imprimante.\n\nSélectionnez votre imprimante dans la liste ci-dessous :"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:96
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:42
@ -1777,13 +1761,13 @@ msgstr "Afficher les tâches d'impression"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:37
msgctxt "@label:status"
msgid "Preparing to print"
msgstr "Préparation de l'impression"
msgstr "Préparation..."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:39
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:263
msgctxt "@label:status"
msgid "Printing"
msgstr "Impression"
msgstr "Impression..."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:41
msgctxt "@label:status"
@ -1824,7 +1808,7 @@ msgstr "Terminé"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:392
msgctxt "@label"
msgid "Preparing to print"
msgstr "Préparation de l'impression"
msgstr "Préparation de l'impression..."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:273
msgctxt "@label:status"
@ -2015,22 +1999,22 @@ msgstr "Modifier les scripts de post-traitement actifs"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/MoreInfoWindow.qml:16
msgctxt "@title:window"
msgid "More information on anonymous data collection"
msgstr ""
msgstr "Plus d'informations sur la collecte de données anonymes"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/MoreInfoWindow.qml:66
msgctxt "@text:window"
msgid "Cura sends anonymous data to Ultimaker in order to improve the print quality and user experience. Below is an example of all the data that is sent."
msgstr ""
msgstr "Cura envoie des données anonymes à Ultimaker afin d'améliorer la qualité d'impression et l'expérience utilisateur. Voici un exemple de toutes les données envoyées."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/MoreInfoWindow.qml:101
msgctxt "@text:window"
msgid "I don't want to send these data"
msgstr ""
msgstr "Je ne veux pas envoyer ces données"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/MoreInfoWindow.qml:111
msgctxt "@text:window"
msgid "Allow sending these data to Ultimaker and help us improve Cura"
msgstr ""
msgstr "Permettre l'envoi de ces données à Ultimaker et nous aider à améliorer Cura"
#: /home/ruben/Projects/Cura/plugins/ImageReader/ConfigUI.qml:19
msgctxt "@title:window"
@ -2544,9 +2528,7 @@ msgctxt "@text:window"
msgid ""
"You have customized some profile settings.\n"
"Would you like to keep or discard those settings?"
msgstr ""
"Vous avez personnalisé certains paramètres du profil.\n"
"Souhaitez-vous conserver ces changements, ou les annuler ?"
msgstr "Vous avez personnalisé certains paramètres du profil.\nSouhaitez-vous conserver ces changements, ou les annuler ?"
#: /home/ruben/Projects/Cura/resources/qml/DiscardOrKeepProfileChangesDialog.qml:110
msgctxt "@title:column"
@ -2609,7 +2591,7 @@ msgstr "Confirmer le changement de diamètre"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MaterialView.qml:95
msgctxt "@label (%1 is a number)"
msgid "The new filament diameter is set to %1 mm, which is not compatible with the current extruder. Do you wish to continue?"
msgstr ""
msgstr "Le nouveau diamètre de filament est réglé sur %1 mm, ce qui n'est pas compatible avec l'extrudeuse actuelle. Souhaitez-vous poursuivre ?"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MaterialView.qml:128
msgctxt "@label"
@ -2880,12 +2862,12 @@ msgstr "Mettre à l'échelle les modèles extrêmement petits"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:507
msgctxt "@info:tooltip"
msgid "Should models be selected after they are loaded?"
msgstr ""
msgstr "Les modèles doivent-ils être sélectionnés après leur chargement ?"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:512
msgctxt "@option:check"
msgid "Select models when loaded"
msgstr ""
msgstr "Sélectionner les modèles lorsqu'ils sont chargés"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:522
msgctxt "@info:tooltip"
@ -2970,7 +2952,7 @@ msgstr "Envoyer des informations (anonymes) sur l'impression"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:708
msgctxt "@action:button"
msgid "More information"
msgstr ""
msgstr "Plus d'informations"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:726
msgctxt "@label"
@ -3246,9 +3228,7 @@ msgctxt "@info:credit"
msgid ""
"Cura is developed by Ultimaker B.V. in cooperation with the community.\n"
"Cura proudly uses the following open source projects:"
msgstr ""
"Cura a été développé par Ultimaker B.V. en coopération avec la communauté Ultimaker.\n"
"Cura est fier d'utiliser les projets open source suivants :"
msgstr "Cura a été développé par Ultimaker B.V. en coopération avec la communauté Ultimaker.\nCura est fier d'utiliser les projets open source suivants :"
#: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:118
msgctxt "@label"
@ -3361,10 +3341,7 @@ msgid ""
"Some setting/override values are different from the values stored in the profile.\n"
"\n"
"Click to open the profile manager."
msgstr ""
"Certaines valeurs de paramètre / forçage sont différentes des valeurs enregistrées dans le profil. \n"
"\n"
"Cliquez pour ouvrir le gestionnaire de profils."
msgstr "Certaines valeurs de paramètre / forçage sont différentes des valeurs enregistrées dans le profil. \n\nCliquez pour ouvrir le gestionnaire de profils."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:199
msgctxt "@label:textbox"
@ -3405,12 +3382,12 @@ msgstr "Configurer la visibilité des paramètres..."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:621
msgctxt "@action:inmenu"
msgid "Collapse All"
msgstr ""
msgstr "Réduire tout"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:626
msgctxt "@action:inmenu"
msgid "Expand All"
msgstr ""
msgstr "Développer tout"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingCategory.qml:249
msgctxt "@label"
@ -3418,20 +3395,17 @@ msgid ""
"Some hidden settings use values different from their normal calculated value.\n"
"\n"
"Click to make these settings visible."
msgstr ""
"Certains paramètres masqués utilisent des valeurs différentes de leur valeur normalement calculée.\n"
"\n"
"Cliquez pour rendre ces paramètres visibles."
msgstr "Certains paramètres masqués utilisent des valeurs différentes de leur valeur normalement calculée.\n\nCliquez pour rendre ces paramètres visibles."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:61
msgctxt "@label Header for list of settings."
msgid "Affects"
msgstr "Dirige"
msgstr "Touche"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:66
msgctxt "@label Header for list of settings."
msgid "Affected By"
msgstr "Est Dirigé Par"
msgstr "Touché par"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:154
msgctxt "@label"
@ -3449,10 +3423,7 @@ msgid ""
"This setting has a value that is different from the profile.\n"
"\n"
"Click to restore the value of the profile."
msgstr ""
"Ce paramètre possède une valeur qui est différente du profil.\n"
"\n"
"Cliquez pour restaurer la valeur du profil."
msgstr "Ce paramètre possède une valeur qui est différente du profil.\n\nCliquez pour restaurer la valeur du profil."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:286
msgctxt "@label"
@ -3460,10 +3431,7 @@ msgid ""
"This setting is normally calculated, but it currently has an absolute value set.\n"
"\n"
"Click to restore the calculated value."
msgstr ""
"Ce paramètre est normalement calculé mais il possède actuellement une valeur absolue définie.\n"
"\n"
"Cliquez pour restaurer la valeur calculée."
msgstr "Ce paramètre est normalement calculé mais il possède actuellement une valeur absolue définie.\n\nCliquez pour restaurer la valeur calculée."
#: /home/ruben/Projects/Cura/resources/qml/PrinterOutput/ManualPrinterControl.qml:129
msgctxt "@label"
@ -3641,7 +3609,7 @@ msgstr "Nombre de copies"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml:33
msgctxt "@label:header configurations"
msgid "Available configurations"
msgstr "Configurations disponibles"
msgstr "Configurations disponibles :"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml:28
msgctxt "@label:extruder label"
@ -3651,12 +3619,12 @@ msgstr "Extrudeuse"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ConfigurationMenu/SyncButton.qml:16
msgctxt "@label:extruder label"
msgid "Yes"
msgstr ""
msgstr "Oui"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ConfigurationMenu/SyncButton.qml:16
msgctxt "@label:extruder label"
msgid "No"
msgstr ""
msgstr "Non"
#: /home/ruben/Projects/Cura/resources/qml/Menus/RecentFilesMenu.qml:13
msgctxt "@title:menu menubar:file"
@ -3673,9 +3641,7 @@ msgctxt "@label:listbox"
msgid ""
"Print Setup disabled\n"
"G-code files cannot be modified"
msgstr ""
"Configuration de l'impression désactivée\n"
"Les fichiers G-Code ne peuvent pas être modifiés"
msgstr "Configuration de l'impression désactivée\nLes fichiers G-Code ne peuvent pas être modifiés"
#: /home/ruben/Projects/Cura/resources/qml/PrepareSidebar.qml:341
msgctxt "@label Hours and minutes"
@ -3954,7 +3920,7 @@ msgstr "Afficher le dossier de configuration"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:433
msgctxt "@action:menu"
msgid "Browse packages..."
msgstr ""
msgstr "Parcourir les paquets..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:440
msgctxt "@action:inmenu menubar:view"
@ -4112,7 +4078,7 @@ msgstr "E&xtensions"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:274
msgctxt "@title:menu menubar:toplevel"
msgid "&Toolbox"
msgstr ""
msgstr "&Boîte à outils"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:281
msgctxt "@title:menu menubar:toplevel"
@ -4127,7 +4093,7 @@ msgstr "&Aide"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:335
msgctxt "@label"
msgid "This package will be installed after restarting."
msgstr ""
msgstr "Ce paquet sera installé après le redémarrage."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:364
msgctxt "@action:button"
@ -4152,7 +4118,7 @@ msgstr "Êtes-vous sûr(e) de souhaiter lancer un nouveau projet ? Cela supprim
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:814
msgctxt "@window:title"
msgid "Install Package"
msgstr ""
msgstr "Installer le paquet"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:821
msgctxt "@title:window"
@ -4360,7 +4326,7 @@ msgstr "Réorganiser le plateau actuel"
#: MachineSettingsAction/plugin.json
msgctxt "description"
msgid "Provides a way to change machine settings (such as build volume, nozzle size, etc.)."
msgstr ""
msgstr "Permet de modifier les paramètres de la machine (tels que volume d'impression, taille de buse, etc.)"
#: MachineSettingsAction/plugin.json
msgctxt "name"
@ -4370,12 +4336,12 @@ msgstr "Action Paramètres de la machine"
#: Toolbox/plugin.json
msgctxt "description"
msgid "Find, manage and install new Cura packages."
msgstr ""
msgstr "Rechercher, gérer et installer de nouveaux paquets Cura."
#: Toolbox/plugin.json
msgctxt "name"
msgid "Toolbox"
msgstr ""
msgstr "Boîte à outils"
#: XRayView/plugin.json
msgctxt "description"
@ -4470,7 +4436,7 @@ msgstr "Impression par USB"
#: UserAgreement/plugin.json
msgctxt "description"
msgid "Ask the user once if he/she agrees with our license."
msgstr ""
msgstr "Demander à l'utilisateur une fois s'il appose son accord à notre licence"
#: UserAgreement/plugin.json
msgctxt "name"
@ -4480,12 +4446,12 @@ msgstr "UserAgreement"
#: X3GWriter/plugin.json
msgctxt "description"
msgid "Allows saving the resulting slice as an X3G file, to support printers that read this format (Malyan, Makerbot and other Sailfish-based printers)."
msgstr ""
msgstr "Permet de sauvegarder la tranche résultante sous forme de fichier X3G, pour prendre en charge les imprimantes qui lisent ce format (Malyan, Makerbot et autres imprimantes basées sur Sailfish)."
#: X3GWriter/plugin.json
msgctxt "name"
msgid "X3GWriter"
msgstr ""
msgstr "X3GWriter"
#: GCodeGzWriter/plugin.json
msgctxt "description"
@ -4540,7 +4506,7 @@ msgstr "Plugin de périphérique de sortie sur disque amovible"
#: UM3NetworkPrinting/plugin.json
msgctxt "description"
msgid "Manages network connections to Ultimaker 3 printers."
msgstr ""
msgstr "Gère les connexions réseau vers les imprimantes Ultimaker 3"
#: UM3NetworkPrinting/plugin.json
msgctxt "name"
@ -4670,12 +4636,12 @@ msgstr "Mise à niveau de 3.2 vers 3.3"
#: VersionUpgrade/VersionUpgrade33to34/plugin.json
msgctxt "description"
msgid "Upgrades configurations from Cura 3.3 to Cura 3.4."
msgstr ""
msgstr "Configurations des mises à niveau de Cura 3.3 vers Cura 3.4."
#: VersionUpgrade/VersionUpgrade33to34/plugin.json
msgctxt "name"
msgid "Version Upgrade 3.3 to 3.4"
msgstr ""
msgstr "Mise à niveau de 3.3 vers 3.4"
#: VersionUpgrade/VersionUpgrade25to26/plugin.json
msgctxt "description"
@ -4820,7 +4786,7 @@ msgstr "Générateur 3MF"
#: UltimakerMachineActions/plugin.json
msgctxt "description"
msgid "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.)."
msgstr ""
msgstr "Fournit les actions de la machine pour les machines Ultimaker (telles que l'assistant de calibration du plateau, sélection des mises à niveau, etc.)"
#: UltimakerMachineActions/plugin.json
msgctxt "name"

View file

@ -57,9 +57,7 @@ msgctxt "machine_start_gcode description"
msgid ""
"G-code commands to be executed at the very start - separated by \n"
"."
msgstr ""
"Commandes G-Code à exécuter au tout début, séparées par \n"
"."
msgstr "Commandes G-Code à exécuter au tout début, séparées par \n."
#: fdmprinter.def.json
msgctxt "machine_end_gcode label"
@ -71,9 +69,7 @@ msgctxt "machine_end_gcode description"
msgid ""
"G-code commands to be executed at the very end - separated by \n"
"."
msgstr ""
"Commandes G-Code à exécuter tout à la fin, séparées par \n"
"."
msgstr "Commandes G-Code à exécuter tout à la fin, séparées par \n."
#: fdmprinter.def.json
msgctxt "material_guid label"
@ -473,7 +469,7 @@ msgstr "Appliquer le décalage de l'extrudeuse au système de coordonnées."
#: fdmprinter.def.json
msgctxt "extruder_prime_pos_z label"
msgid "Extruder Prime Z Position"
msgstr "Extrudeuse position d'amorçage Z"
msgstr "Extrudeuse Position d'amorçage Z"
#: fdmprinter.def.json
msgctxt "extruder_prime_pos_z description"
@ -793,12 +789,12 @@ msgstr "Largeur d'une seule ligne de remplissage."
#: fdmprinter.def.json
msgctxt "skirt_brim_line_width label"
msgid "Skirt/Brim Line Width"
msgstr "Largeur des lignes de contour/bordure"
msgstr "Largeur des lignes de jupe/bordure"
#: fdmprinter.def.json
msgctxt "skirt_brim_line_width description"
msgid "Width of a single skirt or brim line."
msgstr "Largeur d'une seule ligne de contour ou de bordure."
msgstr "Largeur d'une seule ligne de jupe ou de bordure."
#: fdmprinter.def.json
msgctxt "support_line_width label"
@ -1088,7 +1084,7 @@ msgstr "Optimiser l'ordre d'impression des parois"
#: fdmprinter.def.json
msgctxt "optimize_wall_printing_order description"
msgid "Optimize the order in which walls are printed so as to reduce the number of retractions and the distance travelled. Most parts will benefit from this being enabled but some may actually take longer so please compare the print time estimates with and without optimization. First layer is not optimized when choosing brim as build plate adhesion type."
msgstr ""
msgstr "Optimiser l'ordre dans lequel des parois sont imprimées de manière à réduire le nombre de retraits et les distances parcourues. La plupart des pièces bénéficieront de cette possibilité, mais certaines peuvent en fait prendre plus de temps à l'impression ; veuillez dès lors comparer les estimations de durée d'impression avec et sans optimisation. La première couche n'est pas optimisée lorsque le type d'adhérence au plateau est défini sur bordure."
#: fdmprinter.def.json
msgctxt "outer_inset_first label"
@ -1678,22 +1674,22 @@ msgstr "Ne pas générer de zones de remplissage plus petites que cela (utiliser
#: fdmprinter.def.json
msgctxt "infill_support_enabled label"
msgid "Infill Support"
msgstr ""
msgstr "Support de remplissage"
#: fdmprinter.def.json
msgctxt "infill_support_enabled description"
msgid "Print infill structures only where tops of the model should be supported. Enabling this reduces print time and material usage, but leads to ununiform object strength."
msgstr ""
msgstr "Imprimer les structures de remplissage uniquement là où le haut du modèle doit être supporté, ce qui permet de réduire le temps d'impression et l'utilisation de matériau, mais conduit à une résistance uniforme de l'objet."
#: fdmprinter.def.json
msgctxt "infill_support_angle label"
msgid "Infill Overhang Angle"
msgstr ""
msgstr "Angle de porte-à-faux de remplissage"
#: fdmprinter.def.json
msgctxt "infill_support_angle description"
msgid "The minimum angle of internal overhangs for which infill is added. At a value of 0° objects are totally filled with infill, 90° will not provide any infill."
msgstr ""
msgstr "Angle minimal des porte-à-faux internes pour lesquels le remplissage est ajouté. À une valeur de 0°, les objets sont totalement remplis, 90° ne fournira aucun remplissage."
#: fdmprinter.def.json
msgctxt "skin_preshrink label"
@ -2038,12 +2034,12 @@ msgstr "L'intervalle dans lequel le nombre maximal de rétractions est incrémen
#: fdmprinter.def.json
msgctxt "limit_support_retractions label"
msgid "Limit Support Retractions"
msgstr ""
msgstr "Limiter les rétractations du support"
#: fdmprinter.def.json
msgctxt "limit_support_retractions description"
msgid "Omit retraction when moving from support to support in a straight line. Enabling this setting saves print time, but can lead to excesive stringing within the support structure."
msgstr ""
msgstr "Omettre la rétraction lors du passage entre supports en ligne droite. L'activation de ce paramètre permet de gagner du temps lors de l'impression, mais peut conduire à un cordage excessif à l'intérieur de la structure de support."
#: fdmprinter.def.json
msgctxt "material_standby_temperature label"
@ -2278,12 +2274,12 @@ msgstr "Vitesse des mouvements de déplacement dans la couche initiale. Une vale
#: fdmprinter.def.json
msgctxt "skirt_brim_speed label"
msgid "Skirt/Brim Speed"
msgstr "Vitesse d'impression du contour/bordure"
msgstr "Vitesse d'impression de la jupe/bordure"
#: fdmprinter.def.json
msgctxt "skirt_brim_speed description"
msgid "The speed at which the skirt and brim are printed. Normally this is done at the initial layer speed, but sometimes you might want to print the skirt or brim at a different speed."
msgstr "La vitesse à laquelle le contour et la bordure sont imprimées. Normalement, cette vitesse est celle de la couche initiale, mais il est parfois nécessaire dimprimer le contour ou la bordure à une vitesse différente."
msgstr "La vitesse à laquelle la jupe et la bordure sont imprimées. Normalement, cette vitesse est celle de la couche initiale, mais il est parfois nécessaire dimprimer la jupe ou la bordure à une vitesse différente."
#: fdmprinter.def.json
msgctxt "max_feedrate_z_override label"
@ -2508,12 +2504,12 @@ msgstr "L'accélération pour les déplacements dans la couche initiale."
#: fdmprinter.def.json
msgctxt "acceleration_skirt_brim label"
msgid "Skirt/Brim Acceleration"
msgstr "Accélération du contour/bordure"
msgstr "Accélération de la jupe/bordure"
#: fdmprinter.def.json
msgctxt "acceleration_skirt_brim description"
msgid "The acceleration with which the skirt and brim are printed. Normally this is done with the initial layer acceleration, but sometimes you might want to print the skirt or brim at a different acceleration."
msgstr "L'accélération selon laquelle le contour et la bordure sont imprimées. Normalement, cette accélération est celle de la couche initiale, mais il est parfois nécessaire dimprimer le contour ou la bordure à une accélération différente."
msgstr "L'accélération selon laquelle la jupe et la bordure sont imprimées. Normalement, cette accélération est celle de la couche initiale, mais il est parfois nécessaire dimprimer la jupe ou la bordure à une accélération différente."
#: fdmprinter.def.json
msgctxt "jerk_enabled label"
@ -2698,12 +2694,12 @@ msgstr "L'accélération pour les déplacements dans la couche initiale."
#: fdmprinter.def.json
msgctxt "jerk_skirt_brim label"
msgid "Skirt/Brim Jerk"
msgstr "Saccade du contour/bordure"
msgstr "Saccade de la jupe/bordure"
#: fdmprinter.def.json
msgctxt "jerk_skirt_brim description"
msgid "The maximum instantaneous velocity change with which the skirt and brim are printed."
msgstr "Le changement instantané maximal de vitesse selon lequel le contour et la bordure sont imprimées."
msgstr "Le changement instantané maximal de vitesse selon lequel la jupe et la bordure sont imprimées."
#: fdmprinter.def.json
msgctxt "travel label"
@ -2738,17 +2734,17 @@ msgstr "Tout"
#: fdmprinter.def.json
msgctxt "retraction_combing option noskin"
msgid "Not in Skin"
msgstr ""
msgstr "Pas dans la couche extérieure"
#: fdmprinter.def.json
msgctxt "retraction_combing_max_distance label"
msgid "Max Comb Distance With No Retract"
msgstr ""
msgstr "Distance de détour max. sans rétraction"
#: fdmprinter.def.json
msgctxt "retraction_combing_max_distance description"
msgid "When non-zero, combing travel moves that are longer than this distance will use retraction."
msgstr ""
msgstr "Lorsque cette distance n'est pas nulle, les déplacements de détour qui sont plus longs que cette distance utiliseront la rétraction."
#: fdmprinter.def.json
msgctxt "travel_retract_before_outer_wall label"
@ -2773,12 +2769,12 @@ msgstr "La buse contourne les pièces déjà imprimées lorsqu'elle se déplace.
#: fdmprinter.def.json
msgctxt "travel_avoid_supports label"
msgid "Avoid Supports When Traveling"
msgstr ""
msgstr "Éviter les supports lors du déplacement"
#: fdmprinter.def.json
msgctxt "travel_avoid_supports description"
msgid "The nozzle avoids already printed supports when traveling. This option is only available when combing is enabled."
msgstr ""
msgstr "La buse contourne les supports déjà imprimés lorsqu'elle se déplace. Cette option est disponible uniquement lorsque les détours sont activés."
#: fdmprinter.def.json
msgctxt "travel_avoid_distance label"
@ -3138,12 +3134,12 @@ msgstr "Entrecroisé"
#: fdmprinter.def.json
msgctxt "support_wall_count label"
msgid "Support Wall Line Count"
msgstr ""
msgstr "Nombre de lignes de la paroi du support"
#: fdmprinter.def.json
msgctxt "support_wall_count description"
msgid "The number of walls with which to surround support infill. Adding a wall can make support print more reliably and can support overhangs better, but increases print time and material used."
msgstr ""
msgstr "Nombre de parois qui entourent le remplissage de support. L'ajout d'une paroi peut rendre l'impression de support plus fiable et mieux supporter les porte-à-faux, mais augmente le temps d'impression et la quantité de matériau nécessaire."
#: fdmprinter.def.json
msgctxt "zig_zaggify_support label"
@ -3633,7 +3629,7 @@ msgstr "Activer la goutte de préparation"
#: fdmprinter.def.json
msgctxt "prime_blob_enable description"
msgid "Whether to prime the filament with a blob before printing. Turning this setting on will ensure that the extruder will have material ready at the nozzle before printing. Printing Brim or Skirt can act like priming too, in which case turning this setting off saves some time."
msgstr "Préparer les filaments avec une goutte avant l'impression. Ce paramètre permet d'assurer que l'extrudeuse disposera de matériau prêt au niveau de la buse avant l'impression. Le contour/bordure d'impression peut également servir de préparation, auquel cas le fait de laisser ce paramètre désactivé permet de gagner un peu de temps."
msgstr "Préparer les filaments avec une goutte avant l'impression. Ce paramètre permet d'assurer que l'extrudeuse disposera de matériau prêt au niveau de la buse avant l'impression. La jupe/bordure d'impression peut également servir de préparation, auquel cas le fait de laisser ce paramètre désactivé permet de gagner un peu de temps."
#: fdmprinter.def.json
msgctxt "extruder_prime_pos_x label"
@ -3663,12 +3659,12 @@ msgstr "Type d'adhérence du plateau"
#: fdmprinter.def.json
msgctxt "adhesion_type description"
msgid "Different options that help to improve both priming your extrusion and adhesion to the build plate. Brim adds a single layer flat area around the base of your model to prevent warping. Raft adds a thick grid with a roof below the model. Skirt is a line printed around the model, but not connected to the model."
msgstr "Différentes options qui permettent d'améliorer la préparation de votre extrusion et l'adhérence au plateau. La bordure ajoute une zone plate d'une seule couche autour de la base de votre modèle, afin de l'empêcher de se redresser. Le radeau ajoute une grille épaisse avec un toit sous le modèle. Le contour est une ligne imprimée autour du modèle mais qui n'est pas rattachée au modèle."
msgstr "Différentes options qui permettent d'améliorer la préparation de votre extrusion et l'adhérence au plateau. La bordure ajoute une zone plate d'une seule couche autour de la base de votre modèle, afin de l'empêcher de se redresser. Le radeau ajoute une grille épaisse avec un toit sous le modèle. La jupe est une ligne imprimée autour du modèle mais qui n'est pas rattachée au modèle."
#: fdmprinter.def.json
msgctxt "adhesion_type option skirt"
msgid "Skirt"
msgstr "Contour"
msgstr "Jupe"
#: fdmprinter.def.json
msgctxt "adhesion_type option brim"
@ -3693,41 +3689,39 @@ msgstr "Extrudeuse d'adhérence du plateau"
#: fdmprinter.def.json
msgctxt "adhesion_extruder_nr description"
msgid "The extruder train to use for printing the skirt/brim/raft. This is used in multi-extrusion."
msgstr "Le train d'extrudeuse à utiliser pour l'impression du contour/de la bordure/du radeau. Cela est utilisé en multi-extrusion."
msgstr "Le train d'extrudeuse à utiliser pour l'impression de la jupe/la bordure/du radeau. Cela est utilisé en multi-extrusion."
#: fdmprinter.def.json
msgctxt "skirt_line_count label"
msgid "Skirt Line Count"
msgstr "Nombre de lignes du contour"
msgstr "Nombre de lignes de la jupe"
#: fdmprinter.def.json
msgctxt "skirt_line_count description"
msgid "Multiple skirt lines help to prime your extrusion better for small models. Setting this to 0 will disable the skirt."
msgstr "Un contour à plusieurs lignes vous aide à mieux préparer votre extrusion pour les petits modèles. Définissez celle valeur sur 0 pour désactiver le contour."
msgstr "Une jupe à plusieurs lignes vous aide à mieux préparer votre extrusion pour les petits modèles. Définissez celle valeur sur 0 pour désactiver la jupe."
#: fdmprinter.def.json
msgctxt "skirt_gap label"
msgid "Skirt Distance"
msgstr "Distance du contour"
msgstr "Distance de la jupe"
#: fdmprinter.def.json
msgctxt "skirt_gap description"
msgid ""
"The horizontal distance between the skirt and the first layer of the print.\n"
"This is the minimum distance. Multiple skirt lines will extend outwards from this distance."
msgstr ""
"La distance horizontale entre le contour et la première couche de limpression.\n"
"Il sagit de la distance minimale séparant le contour de lobjet. Si le contour a dautres lignes, celles-ci sétendront vers lextérieur."
msgstr "La distance horizontale entre la jupe et la première couche de limpression.\nIl sagit de la distance minimale séparant la jupe de lobjet. Si la jupe a dautres lignes, celles-ci sétendront vers lextérieur."
#: fdmprinter.def.json
msgctxt "skirt_brim_minimal_length label"
msgid "Skirt/Brim Minimum Length"
msgstr "Longueur minimale du contour/bordure"
msgstr "Longueur minimale de la jupe/bordure"
#: fdmprinter.def.json
msgctxt "skirt_brim_minimal_length description"
msgid "The minimum length of the skirt or brim. If this length is not reached by all skirt or brim lines together, more skirt or brim lines will be added until the minimum length is reached. Note: If the line count is set to 0 this is ignored."
msgstr "La longueur minimale du contour ou bordure. Si cette longueur nest pas atteinte par toutes les lignes de contour ou de bordure ensemble, dautres lignes de contour ou de bordure seront ajoutées afin datteindre la longueur minimale. Veuillez noter que si le nombre de lignes est défini sur 0, cette option est ignorée."
msgstr "La longueur minimale de la jupe ou bordure. Si cette longueur nest pas atteinte par toutes les lignes de jupe ou de bordure ensemble, dautres lignes de jupe ou de bordure seront ajoutées afin datteindre la longueur minimale. Veuillez noter que si le nombre de lignes est défini sur 0, cette option est ignorée."
#: fdmprinter.def.json
msgctxt "brim_width label"
@ -4677,12 +4671,12 @@ msgstr "Taille minimum d'un segment de ligne après découpage. Si vous augmente
#: fdmprinter.def.json
msgctxt "meshfix_maximum_travel_resolution label"
msgid "Maximum Travel Resolution"
msgstr ""
msgstr "Résolution de déplacement maximum"
#: fdmprinter.def.json
msgctxt "meshfix_maximum_travel_resolution description"
msgid "The minimum size of a travel line segment after slicing. If you increase this, the travel moves will have less smooth corners. This may allow the printer to keep up with the speed it has to process g-code, but it may cause model avoidance to become less accurate."
msgstr ""
msgstr "Taille minimale d'un segment de ligne de déplacement après la découpe. Si vous augmentez cette valeur, les mouvements de déplacement auront des coins moins lisses. Cela peut permettre à l'imprimante de suivre la vitesse à laquelle elle doit traiter le G-Code, mais cela peut réduire la précision de l'évitement du modèle."
#: fdmprinter.def.json
msgctxt "support_skip_some_zags label"
@ -4847,22 +4841,22 @@ msgstr "La taille de poches aux croisements à quatre branches dans le motif ent
#: fdmprinter.def.json
msgctxt "cross_infill_density_image label"
msgid "Cross Infill Density Image"
msgstr ""
msgstr "Image de densité du remplissage croisé"
#: fdmprinter.def.json
msgctxt "cross_infill_density_image description"
msgid "The file location of an image of which the brightness values determine the minimal density at the corresponding location in the infill of the print."
msgstr ""
msgstr "Emplacement du fichier d'une image dont les valeurs de luminosité déterminent la densité minimale à l'emplacement correspondant dans le remplissage de l'impression."
#: fdmprinter.def.json
msgctxt "cross_support_density_image label"
msgid "Cross Fill Density Image for Support"
msgstr ""
msgstr "Image de densité du remplissage croisé pour le support"
#: fdmprinter.def.json
msgctxt "cross_support_density_image description"
msgid "The file location of an image of which the brightness values determine the minimal density at the corresponding location in the support."
msgstr ""
msgstr "Emplacement du fichier d'une image dont les valeurs de luminosité déterminent la densité minimale à l'emplacement correspondant dans le support."
#: fdmprinter.def.json
msgctxt "spaghetti_infill_enabled label"
@ -5174,9 +5168,7 @@ msgctxt "wireframe_up_half_speed description"
msgid ""
"Distance of an upward move which is extruded with half speed.\n"
"This can cause better adhesion to previous layers, while not heating the material in those layers too much. Only applies to Wire Printing."
msgstr ""
"Distance dun déplacement ascendant qui est extrudé à mi-vitesse.\n"
"Cela peut permettre une meilleure adhérence aux couches précédentes sans surchauffer le matériau dans ces couches. Uniquement applicable à l'impression filaire."
msgstr "Distance dun déplacement ascendant qui est extrudé à mi-vitesse.\nCela peut permettre une meilleure adhérence aux couches précédentes sans surchauffer le matériau dans ces couches. Uniquement applicable à l'impression filaire."
#: fdmprinter.def.json
msgctxt "wireframe_top_jump label"
@ -5301,7 +5293,7 @@ msgstr "Variation maximale des couches adaptatives"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation description"
msgid "The maximum allowed height different from the base layer height."
msgstr ""
msgstr "Hauteur maximale autorisée par rapport à la couche de base."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step label"
@ -5536,7 +5528,7 @@ msgstr "Paramètres qui sont utilisés uniquement si CuraEngine n'est pas invoqu
#: fdmprinter.def.json
msgctxt "center_object label"
msgid "Center Object"
msgstr ""
msgstr "Centrer l'objet"
#: fdmprinter.def.json
msgctxt "center_object description"
@ -5546,7 +5538,7 @@ msgstr "S'il faut centrer l'objet au milieu du plateau d'impression (0,0) au lie
#: fdmprinter.def.json
msgctxt "mesh_position_x label"
msgid "Mesh Position X"
msgstr ""
msgstr "Position X de la maille"
#: fdmprinter.def.json
msgctxt "mesh_position_x description"
@ -5556,7 +5548,7 @@ msgstr "Offset appliqué à l'objet dans la direction X."
#: fdmprinter.def.json
msgctxt "mesh_position_y label"
msgid "Mesh Position Y"
msgstr ""
msgstr "Position Y de la maille"
#: fdmprinter.def.json
msgctxt "mesh_position_y description"
@ -5566,7 +5558,7 @@ msgstr "Offset appliqué à l'objet dans la direction Y."
#: fdmprinter.def.json
msgctxt "mesh_position_z label"
msgid "Mesh Position Z"
msgstr ""
msgstr "Position Z de la maille"
#: fdmprinter.def.json
msgctxt "mesh_position_z description"

View file

@ -41,7 +41,7 @@ msgstr "File G-Code"
#: /home/ruben/Projects/Cura/plugins/ModelChecker/ModelChecker.py:30
msgctxt "@info:title"
msgid "3D Model Assistant"
msgstr ""
msgstr "Assistente modello 3D"
#: /home/ruben/Projects/Cura/plugins/ModelChecker/ModelChecker.py:80
#, python-brace-format
@ -51,7 +51,7 @@ msgid ""
"<p>{model_names}</p>\n"
"<p>Find out how to ensure the best possible print quality and reliability.</p>\n"
"<p><a href=\"https://ultimaker.com/3D-model-assistant\">View print quality guide</a></p>"
msgstr ""
msgstr "<p>La stampa di uno o più modelli 3D può non avvenire in modo ottimale a causa della dimensioni modello e della configurazione materiale:</p>\n<p>{model_names}</p>\n<p>Scopri come garantire la migliore qualità ed affidabilità di stampa.</p>\n<p><a href=\"https://ultimaker.com/3D-model-assistant\">Visualizza la guida alla qualità di stampa</a></p>"
#: /home/ruben/Projects/Cura/plugins/Doodle3D-cura-plugin/Doodle3D/D3DCloudPrintOutputDevicePlugin.py:65
msgctxt "@action:button"
@ -158,12 +158,12 @@ msgstr "File X3G"
#: /home/ruben/Projects/Cura/plugins/X3GWriter/build/GPX-prefix/src/GPX/slicerplugins/cura15.06/X3gWriter/__init__.py:16
msgctxt "X3g Writer Plugin Description"
msgid "Writes X3g to files"
msgstr ""
msgstr "Scrive X3g sui file"
#: /home/ruben/Projects/Cura/plugins/X3GWriter/build/GPX-prefix/src/GPX/slicerplugins/cura15.06/X3gWriter/__init__.py:21
msgctxt "X3g Writer File Description"
msgid "X3g File"
msgstr ""
msgstr "File X3g"
#: /home/ruben/Projects/Cura/plugins/GCodeGzWriter/__init__.py:17
#: /home/ruben/Projects/Cura/plugins/GCodeGzReader/__init__.py:17
@ -568,12 +568,12 @@ msgstr "Acquisizione dati"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:49
msgctxt "@action:button"
msgid "More info"
msgstr ""
msgstr "Per saperne di più"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:50
msgctxt "@action:tooltip"
msgid "See more information on what data Cura sends."
msgstr ""
msgstr "Vedere ulteriori informazioni sui dati inviati da Cura."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:52
msgctxt "@action:button"
@ -600,9 +600,7 @@ msgctxt "@info:status"
msgid ""
"Could not export using \"{}\" quality!\n"
"Felt back to \"{}\"."
msgstr ""
"Impossibile esportare utilizzando qualità \"{}\" quality!\n"
"Tornato a \"{}\"."
msgstr "Impossibile esportare utilizzando qualità \"{}\" quality!\nTornato a \"{}\"."
#: /home/ruben/Projects/Cura/plugins/ImageReader/__init__.py:14
msgctxt "@item:inlistbox"
@ -1012,22 +1010,22 @@ msgstr "Volume di stampa"
#: /home/ruben/Projects/Cura/cura/Backups/Backup.py:99
msgctxt "@info:backup_failed"
msgid "Could not create archive from user data directory: {}"
msgstr ""
msgstr "Impossibile creare un archivio dalla directory dei dati utente: {}"
#: /home/ruben/Projects/Cura/cura/Backups/Backup.py:104
msgctxt "@info:title"
msgid "Backup"
msgstr ""
msgstr "Backup"
#: /home/ruben/Projects/Cura/cura/Backups/Backup.py:116
msgctxt "@info:backup_failed"
msgid "Tried to restore a Cura backup without having proper data or meta data."
msgstr ""
msgstr "Tentativo di ripristinare un backup di Cura senza dati o metadati appropriati."
#: /home/ruben/Projects/Cura/cura/Backups/Backup.py:126
msgctxt "@info:backup_failed"
msgid "Tried to restore a Cura backup that does not match your current version."
msgstr ""
msgstr "Tentativo di ripristinare un backup di Cura non corrispondente alla versione corrente."
#: /home/ruben/Projects/Cura/cura/MultiplyObjectsJob.py:27
msgctxt "@info:status"
@ -1078,12 +1076,7 @@ msgid ""
" <p>Backups can be found in the configuration folder.</p>\n"
" <p>Please send us this Crash Report to fix the problem.</p>\n"
" "
msgstr ""
"<p><b>Oops, Ultimaker Cura ha rilevato qualcosa che non sembra corretto.</p></b>\n"
" <p>Abbiamo riscontrato un errore irrecuperabile durante lavvio. È stato probabilmente causato da alcuni file di configurazione errati. Suggeriamo di effettuare il backup e ripristinare la configurazione.</p>\n"
" <p>I backup sono contenuti nella cartella configurazione.</p>\n"
" <p>Si prega di inviare questo Rapporto su crash per correggere il problema.</p>\n"
" "
msgstr "<p><b>Oops, Ultimaker Cura ha rilevato qualcosa che non sembra corretto.</p></b>\n <p>Abbiamo riscontrato un errore irrecuperabile durante lavvio. È stato probabilmente causato da alcuni file di configurazione errati. Suggeriamo di effettuare il backup e ripristinare la configurazione.</p>\n <p>I backup sono contenuti nella cartella configurazione.</p>\n <p>Si prega di inviare questo Rapporto su crash per correggere il problema.</p>\n "
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:102
msgctxt "@action:button"
@ -1116,10 +1109,7 @@ msgid ""
"<p><b>A fatal error has occurred in Cura. Please send us this Crash Report to fix the problem</p></b>\n"
" <p>Please use the \"Send report\" button to post a bug report automatically to our servers</p>\n"
" "
msgstr ""
"<p><b>Si è verificato un errore fatale in Cura. Si prega di inviare questo Rapporto su crash per correggere il problema</p></b>\n"
" <p>Usare il pulsante “Invia report\" per inviare automaticamente una segnalazione errore ai nostri server</p>\n"
" "
msgstr "<p><b>Si è verificato un errore fatale in Cura. Si prega di inviare questo Rapporto su crash per correggere il problema</p></b>\n <p>Usare il pulsante “Invia report\" per inviare automaticamente una segnalazione errore ai nostri server</p>\n "
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:177
msgctxt "@title:groupbox"
@ -1433,7 +1423,7 @@ msgstr "Installa"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxErrorPage.qml:16
msgctxt "@info"
msgid "Could not connect to the Cura Package database. Please check your connection."
msgstr ""
msgstr "Impossibile connettersi al database pacchetto Cura. Verificare la connessione."
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml:35
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxHeader.qml:26
@ -1444,17 +1434,17 @@ msgstr "Plugin"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml:79
msgctxt "@label"
msgid "Version"
msgstr ""
msgstr "Versione"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml:85
msgctxt "@label"
msgid "Last updated"
msgstr ""
msgstr "Ultimo aggiornamento"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml:91
msgctxt "@label"
msgid "Author"
msgstr ""
msgstr "Autore"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml:109
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:269
@ -1472,53 +1462,53 @@ msgstr "Aggiorna"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:31
msgctxt "@action:button"
msgid "Updating"
msgstr ""
msgstr "Aggiornamento in corso"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml:46
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:32
msgctxt "@action:button"
msgid "Updated"
msgstr ""
msgstr "Aggiornamento eseguito"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/Toolbox.qml:13
msgctxt "@title"
msgid "Toolbox"
msgstr ""
msgstr "Casella degli strumenti"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxBackColumn.qml:25
msgctxt "@action:button"
msgid "Back"
msgstr ""
msgstr "Indietro"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxFooter.qml:17
msgctxt "@info"
msgid "You will need to restart Cura before changes in packages have effect."
msgstr ""
msgstr "Riavviare Cura per rendere effettive le modifiche apportate ai pacchetti."
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxFooter.qml:32
msgctxt "@info:button"
msgid "Quit Cura"
msgstr ""
msgstr "Esci da Cura"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxHeader.qml:54
msgctxt "@title:tab"
msgid "Installed"
msgstr ""
msgstr "Installa"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:19
msgctxt "@label"
msgid "Will install upon restarting"
msgstr ""
msgstr "L'installazione sarà eseguita al riavvio"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:51
msgctxt "@action:button"
msgid "Downgrade"
msgstr ""
msgstr "Downgrade"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml:51
msgctxt "@action:button"
msgid "Uninstall"
msgstr ""
msgstr "Disinstalla"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml:16
msgctxt "@title:window"
@ -1531,10 +1521,7 @@ msgid ""
"This plugin contains a license.\n"
"You need to accept this license to install this plugin.\n"
"Do you agree with the terms below?"
msgstr ""
"Questo plugin contiene una licenza.\n"
"È necessario accettare questa licenza per poter installare il plugin.\n"
"Accetti i termini sotto riportati?"
msgstr "Questo plugin contiene una licenza.\nÈ necessario accettare questa licenza per poter installare il plugin.\nAccetti i termini sotto riportati?"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml:54
msgctxt "@action:button"
@ -1549,22 +1536,22 @@ msgstr "Non accetto"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml:17
msgctxt "@label"
msgid "Featured"
msgstr ""
msgstr "In primo piano"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml:20
msgctxt "@label"
msgid "Compatibility"
msgstr ""
msgstr "Compatibilità"
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxLoadingPage.qml:16
msgctxt "@info"
msgid "Fetching packages..."
msgstr ""
msgstr "Recupero dei pacchetti..."
#: /home/ruben/Projects/Cura/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml:87
msgctxt "@label"
msgid "Contact"
msgstr ""
msgstr "Contatto"
#: /home/ruben/Projects/Cura/plugins/ModelChecker/ModelChecker.qml:22
msgctxt "@info:tooltip"
@ -1649,10 +1636,7 @@ msgid ""
"To print directly to your printer over the network, please make sure your printer is connected to the network using a network cable or by connecting your printer to your WIFI network. If you don't connect Cura with your printer, you can still use a USB drive to transfer g-code files to your printer.\n"
"\n"
"Select your printer from the list below:"
msgstr ""
"Per stampare direttamente sulla stampante in rete, verificare che la stampante desiderata sia collegata alla rete mediante un cavo di rete o mediante collegamento alla rete WIFI. Se si collega Cura alla stampante, è comunque possibile utilizzare una chiavetta USB per trasferire i file codice G alla stampante.\n"
"\n"
"Selezionare la stampante dallelenco seguente:"
msgstr "Per stampare direttamente sulla stampante in rete, verificare che la stampante desiderata sia collegata alla rete mediante un cavo di rete o mediante collegamento alla rete WIFI. Se si collega Cura alla stampante, è comunque possibile utilizzare una chiavetta USB per trasferire i file codice G alla stampante.\n\nSelezionare la stampante dallelenco seguente:"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:96
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:42
@ -2013,22 +1997,22 @@ msgstr "Modifica script di post-elaborazione attivi"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/MoreInfoWindow.qml:16
msgctxt "@title:window"
msgid "More information on anonymous data collection"
msgstr ""
msgstr "Maggiori informazioni sulla raccolta di dati anonimi"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/MoreInfoWindow.qml:66
msgctxt "@text:window"
msgid "Cura sends anonymous data to Ultimaker in order to improve the print quality and user experience. Below is an example of all the data that is sent."
msgstr ""
msgstr "Cura invia dati anonimi ad Ultimaker per migliorare la qualità di stampa e l'esperienza dell'utente. Di seguito è riportato un esempio dei dati inviati."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/MoreInfoWindow.qml:101
msgctxt "@text:window"
msgid "I don't want to send these data"
msgstr ""
msgstr "Non voglio inviare questi dati"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/MoreInfoWindow.qml:111
msgctxt "@text:window"
msgid "Allow sending these data to Ultimaker and help us improve Cura"
msgstr ""
msgstr "Il consenso all'invio di questi dati ad Ultimaker ci aiuta ad ottimizzare Cura"
#: /home/ruben/Projects/Cura/plugins/ImageReader/ConfigUI.qml:19
msgctxt "@title:window"
@ -2542,9 +2526,7 @@ msgctxt "@text:window"
msgid ""
"You have customized some profile settings.\n"
"Would you like to keep or discard those settings?"
msgstr ""
"Sono state personalizzate alcune impostazioni del profilo.\n"
"Mantenere o eliminare tali impostazioni?"
msgstr "Sono state personalizzate alcune impostazioni del profilo.\nMantenere o eliminare tali impostazioni?"
#: /home/ruben/Projects/Cura/resources/qml/DiscardOrKeepProfileChangesDialog.qml:110
msgctxt "@title:column"
@ -2607,7 +2589,7 @@ msgstr "Conferma modifica diametro"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MaterialView.qml:95
msgctxt "@label (%1 is a number)"
msgid "The new filament diameter is set to %1 mm, which is not compatible with the current extruder. Do you wish to continue?"
msgstr ""
msgstr "Il nuovo diametro del filamento impostato a %1 mm non è compatibile con l'attuale estrusore. Continuare?"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MaterialView.qml:128
msgctxt "@label"
@ -2878,12 +2860,12 @@ msgstr "Ridimensiona i modelli eccessivamente piccoli"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:507
msgctxt "@info:tooltip"
msgid "Should models be selected after they are loaded?"
msgstr ""
msgstr "I modelli devono essere selezionati dopo essere stati caricati?"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:512
msgctxt "@option:check"
msgid "Select models when loaded"
msgstr ""
msgstr "Selezionare i modelli dopo il caricamento"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:522
msgctxt "@info:tooltip"
@ -2968,7 +2950,7 @@ msgstr "Invia informazioni di stampa (anonime)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:708
msgctxt "@action:button"
msgid "More information"
msgstr ""
msgstr "Ulteriori informazioni"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:726
msgctxt "@label"
@ -3244,9 +3226,7 @@ msgctxt "@info:credit"
msgid ""
"Cura is developed by Ultimaker B.V. in cooperation with the community.\n"
"Cura proudly uses the following open source projects:"
msgstr ""
"Cura è stato sviluppato da Ultimaker B.V. in cooperazione con la comunità.\n"
"Cura è orgogliosa di utilizzare i seguenti progetti open source:"
msgstr "Cura è stato sviluppato da Ultimaker B.V. in cooperazione con la comunità.\nCura è orgogliosa di utilizzare i seguenti progetti open source:"
#: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:118
msgctxt "@label"
@ -3359,10 +3339,7 @@ msgid ""
"Some setting/override values are different from the values stored in the profile.\n"
"\n"
"Click to open the profile manager."
msgstr ""
"Alcuni valori di impostazione/esclusione sono diversi dai valori memorizzati nel profilo.\n"
"\n"
"Fare clic per aprire la gestione profili."
msgstr "Alcuni valori di impostazione/esclusione sono diversi dai valori memorizzati nel profilo.\n\nFare clic per aprire la gestione profili."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:199
msgctxt "@label:textbox"
@ -3403,12 +3380,12 @@ msgstr "Configura visibilità delle impostazioni..."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:621
msgctxt "@action:inmenu"
msgid "Collapse All"
msgstr ""
msgstr "Comprimi tutto"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:626
msgctxt "@action:inmenu"
msgid "Expand All"
msgstr ""
msgstr "Espandi tutto"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingCategory.qml:249
msgctxt "@label"
@ -3416,10 +3393,7 @@ msgid ""
"Some hidden settings use values different from their normal calculated value.\n"
"\n"
"Click to make these settings visible."
msgstr ""
"Alcune impostazioni nascoste utilizzano valori diversi dal proprio valore normale calcolato.\n"
"\n"
"Fare clic per rendere visibili queste impostazioni."
msgstr "Alcune impostazioni nascoste utilizzano valori diversi dal proprio valore normale calcolato.\n\nFare clic per rendere visibili queste impostazioni."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:61
msgctxt "@label Header for list of settings."
@ -3447,10 +3421,7 @@ msgid ""
"This setting has a value that is different from the profile.\n"
"\n"
"Click to restore the value of the profile."
msgstr ""
"Questa impostazione ha un valore diverso dal profilo.\n"
"\n"
"Fare clic per ripristinare il valore del profilo."
msgstr "Questa impostazione ha un valore diverso dal profilo.\n\nFare clic per ripristinare il valore del profilo."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:286
msgctxt "@label"
@ -3458,10 +3429,7 @@ msgid ""
"This setting is normally calculated, but it currently has an absolute value set.\n"
"\n"
"Click to restore the calculated value."
msgstr ""
"Questa impostazione normalmente viene calcolata, ma attualmente ha impostato un valore assoluto.\n"
"\n"
"Fare clic per ripristinare il valore calcolato."
msgstr "Questa impostazione normalmente viene calcolata, ma attualmente ha impostato un valore assoluto.\n\nFare clic per ripristinare il valore calcolato."
#: /home/ruben/Projects/Cura/resources/qml/PrinterOutput/ManualPrinterControl.qml:129
msgctxt "@label"
@ -3649,12 +3617,12 @@ msgstr "Estrusore"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ConfigurationMenu/SyncButton.qml:16
msgctxt "@label:extruder label"
msgid "Yes"
msgstr ""
msgstr ""
#: /home/ruben/Projects/Cura/resources/qml/Menus/ConfigurationMenu/SyncButton.qml:16
msgctxt "@label:extruder label"
msgid "No"
msgstr ""
msgstr "No"
#: /home/ruben/Projects/Cura/resources/qml/Menus/RecentFilesMenu.qml:13
msgctxt "@title:menu menubar:file"
@ -3671,9 +3639,7 @@ msgctxt "@label:listbox"
msgid ""
"Print Setup disabled\n"
"G-code files cannot be modified"
msgstr ""
"Impostazione di stampa disabilitata\n"
"I file codice G non possono essere modificati"
msgstr "Impostazione di stampa disabilitata\nI file codice G non possono essere modificati"
#: /home/ruben/Projects/Cura/resources/qml/PrepareSidebar.qml:341
msgctxt "@label Hours and minutes"
@ -3952,7 +3918,7 @@ msgstr "Mostra cartella di configurazione"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:433
msgctxt "@action:menu"
msgid "Browse packages..."
msgstr ""
msgstr "Sfoglia i pacchetti..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:440
msgctxt "@action:inmenu menubar:view"
@ -4110,7 +4076,7 @@ msgstr "Es&tensioni"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:274
msgctxt "@title:menu menubar:toplevel"
msgid "&Toolbox"
msgstr ""
msgstr "&Casella degli strumenti"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:281
msgctxt "@title:menu menubar:toplevel"
@ -4125,7 +4091,7 @@ msgstr "&Help"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:335
msgctxt "@label"
msgid "This package will be installed after restarting."
msgstr ""
msgstr "Questo pacchetto sarà installato dopo il riavvio."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:364
msgctxt "@action:button"
@ -4150,7 +4116,7 @@ msgstr "Sei sicuro di voler aprire un nuovo progetto? Questo cancellerà il pian
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:814
msgctxt "@window:title"
msgid "Install Package"
msgstr ""
msgstr "Installa il pacchetto"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:821
msgctxt "@title:window"
@ -4358,7 +4324,7 @@ msgstr "Sistema il piano di stampa corrente"
#: MachineSettingsAction/plugin.json
msgctxt "description"
msgid "Provides a way to change machine settings (such as build volume, nozzle size, etc.)."
msgstr ""
msgstr "Fornisce un modo per modificare le impostazioni della macchina (come il volume di stampa, la dimensione ugello, ecc.)"
#: MachineSettingsAction/plugin.json
msgctxt "name"
@ -4368,12 +4334,12 @@ msgstr "Azione Impostazioni macchina"
#: Toolbox/plugin.json
msgctxt "description"
msgid "Find, manage and install new Cura packages."
msgstr ""
msgstr "Trova, gestisce ed installa nuovi pacchetti Cura."
#: Toolbox/plugin.json
msgctxt "name"
msgid "Toolbox"
msgstr ""
msgstr "Casella degli strumenti"
#: XRayView/plugin.json
msgctxt "description"
@ -4468,7 +4434,7 @@ msgstr "Stampa USB"
#: UserAgreement/plugin.json
msgctxt "description"
msgid "Ask the user once if he/she agrees with our license."
msgstr ""
msgstr "Chiedere una volta all'utente se accetta la nostra licenza"
#: UserAgreement/plugin.json
msgctxt "name"
@ -4478,12 +4444,12 @@ msgstr "Contratto di licenza"
#: X3GWriter/plugin.json
msgctxt "description"
msgid "Allows saving the resulting slice as an X3G file, to support printers that read this format (Malyan, Makerbot and other Sailfish-based printers)."
msgstr ""
msgstr "Consente di salvare il sezionamento risultante come un file X3G, per supportare le stampanti che leggono questo formato (Malyan, Makerbot ed altre stampanti basate su firmware Sailfish)."
#: X3GWriter/plugin.json
msgctxt "name"
msgid "X3GWriter"
msgstr ""
msgstr "X3GWriter"
#: GCodeGzWriter/plugin.json
msgctxt "description"
@ -4538,7 +4504,7 @@ msgstr "Plugin dispositivo di output unità rimovibile"
#: UM3NetworkPrinting/plugin.json
msgctxt "description"
msgid "Manages network connections to Ultimaker 3 printers."
msgstr ""
msgstr "Gestisce le connessioni di rete alle stampanti Ultimaker 3"
#: UM3NetworkPrinting/plugin.json
msgctxt "name"
@ -4668,12 +4634,12 @@ msgstr "Aggiornamento della versione da 3.2 a 3.3"
#: VersionUpgrade/VersionUpgrade33to34/plugin.json
msgctxt "description"
msgid "Upgrades configurations from Cura 3.3 to Cura 3.4."
msgstr ""
msgstr "Aggiorna le configurazioni da Cura 3.3 a Cura 3.4."
#: VersionUpgrade/VersionUpgrade33to34/plugin.json
msgctxt "name"
msgid "Version Upgrade 3.3 to 3.4"
msgstr ""
msgstr "Aggiornamento della versione da 3.3 a 3.4"
#: VersionUpgrade/VersionUpgrade25to26/plugin.json
msgctxt "description"
@ -4818,7 +4784,7 @@ msgstr "Writer 3MF"
#: UltimakerMachineActions/plugin.json
msgctxt "description"
msgid "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.)."
msgstr ""
msgstr "Fornisce azioni macchina per le macchine Ultimaker (come la procedura guidata di livellamento del piano di stampa, la selezione degli aggiornamenti, ecc.)"
#: UltimakerMachineActions/plugin.json
msgctxt "name"

View file

@ -56,9 +56,7 @@ msgctxt "machine_start_gcode description"
msgid ""
"G-code commands to be executed at the very start - separated by \n"
"."
msgstr ""
"I comandi codice G da eseguire allavvio, separati da \n"
"."
msgstr "I comandi codice G da eseguire allavvio, separati da \n."
#: fdmprinter.def.json
msgctxt "machine_end_gcode label"
@ -70,9 +68,7 @@ msgctxt "machine_end_gcode description"
msgid ""
"G-code commands to be executed at the very end - separated by \n"
"."
msgstr ""
"I comandi codice G da eseguire alla fine, separati da \n"
"."
msgstr "I comandi codice G da eseguire alla fine, separati da \n."
#: fdmprinter.def.json
msgctxt "material_guid label"
@ -1087,7 +1083,7 @@ msgstr "Ottimizzazione sequenza di stampa pareti"
#: fdmprinter.def.json
msgctxt "optimize_wall_printing_order description"
msgid "Optimize the order in which walls are printed so as to reduce the number of retractions and the distance travelled. Most parts will benefit from this being enabled but some may actually take longer so please compare the print time estimates with and without optimization. First layer is not optimized when choosing brim as build plate adhesion type."
msgstr ""
msgstr "Ottimizzare la sequenza di stampa delle pareti in modo da ridurre il numero di retrazioni e la distanza percorsa. L'abilitazione di questa funzione porta vantaggi per la maggior parte dei pezzi; alcuni possono richiedere un maggior tempo di esecuzione; si consiglia di confrontare i tempi di stampa stimati con e senza ottimizzazione. Scegliendo la funzione brim come tipo di adesione del piano di stampa, il primo strato non viene ottimizzato."
#: fdmprinter.def.json
msgctxt "outer_inset_first label"
@ -1677,22 +1673,22 @@ msgstr "Non generare aree di riempimento inferiori a questa (piuttosto usare il
#: fdmprinter.def.json
msgctxt "infill_support_enabled label"
msgid "Infill Support"
msgstr ""
msgstr "Supporto riempimento"
#: fdmprinter.def.json
msgctxt "infill_support_enabled description"
msgid "Print infill structures only where tops of the model should be supported. Enabling this reduces print time and material usage, but leads to ununiform object strength."
msgstr ""
msgstr "Stampare le strutture di riempimento solo laddove è necessario supportare le sommità del modello. L'abilitazione di questa funzione riduce il tempo di stampa e l'utilizzo del materiale, ma comporta una disuniforme resistenza dell'oggetto."
#: fdmprinter.def.json
msgctxt "infill_support_angle label"
msgid "Infill Overhang Angle"
msgstr ""
msgstr "Angolo di sbalzo del riempimento"
#: fdmprinter.def.json
msgctxt "infill_support_angle description"
msgid "The minimum angle of internal overhangs for which infill is added. At a value of 0° objects are totally filled with infill, 90° will not provide any infill."
msgstr ""
msgstr "L'angolo minimo degli sbalzi interni per il quale viene aggiunto il riempimento. Per un valore corrispondente a 0°, gli oggetti sono completamente riempiti di materiale, per un valore corrispondente a 90° non è previsto riempimento."
#: fdmprinter.def.json
msgctxt "skin_preshrink label"
@ -2037,12 +2033,12 @@ msgstr "La finestra in cui è impostato il massimo numero di retrazioni. Questo
#: fdmprinter.def.json
msgctxt "limit_support_retractions label"
msgid "Limit Support Retractions"
msgstr ""
msgstr "Limitazione delle retrazioni del supporto"
#: fdmprinter.def.json
msgctxt "limit_support_retractions description"
msgid "Omit retraction when moving from support to support in a straight line. Enabling this setting saves print time, but can lead to excesive stringing within the support structure."
msgstr ""
msgstr "Omettere la retrazione negli spostamenti da un supporto ad un altro in linea retta. L'abilitazione di questa impostazione riduce il tempo di stampa, ma può comportare un'eccessiva produzione di filamenti all'interno della struttura del supporto."
#: fdmprinter.def.json
msgctxt "material_standby_temperature label"
@ -2737,17 +2733,17 @@ msgstr "Tutto"
#: fdmprinter.def.json
msgctxt "retraction_combing option noskin"
msgid "Not in Skin"
msgstr ""
msgstr "Non nel rivestimento"
#: fdmprinter.def.json
msgctxt "retraction_combing_max_distance label"
msgid "Max Comb Distance With No Retract"
msgstr ""
msgstr "Massima distanza di combing senza retrazione"
#: fdmprinter.def.json
msgctxt "retraction_combing_max_distance description"
msgid "When non-zero, combing travel moves that are longer than this distance will use retraction."
msgstr ""
msgstr "Per un valore diverso da zero, le corse di spostamento in modalità combing superiori a tale distanza utilizzeranno la retrazione."
#: fdmprinter.def.json
msgctxt "travel_retract_before_outer_wall label"
@ -2772,12 +2768,12 @@ msgstr "Durante lo spostamento lugello evita le parti già stampate. Questa o
#: fdmprinter.def.json
msgctxt "travel_avoid_supports label"
msgid "Avoid Supports When Traveling"
msgstr ""
msgstr "Aggiramento dei supporti durante gli spostamenti"
#: fdmprinter.def.json
msgctxt "travel_avoid_supports description"
msgid "The nozzle avoids already printed supports when traveling. This option is only available when combing is enabled."
msgstr ""
msgstr "Durante lo spostamento l'ugello evita i supporti già stampati. Questa opzione è disponibile solo quando è abilitata la funzione combing."
#: fdmprinter.def.json
msgctxt "travel_avoid_distance label"
@ -3137,12 +3133,12 @@ msgstr "Incrociata"
#: fdmprinter.def.json
msgctxt "support_wall_count label"
msgid "Support Wall Line Count"
msgstr ""
msgstr "Numero delle linee perimetrali supporto"
#: fdmprinter.def.json
msgctxt "support_wall_count description"
msgid "The number of walls with which to surround support infill. Adding a wall can make support print more reliably and can support overhangs better, but increases print time and material used."
msgstr ""
msgstr "Il numero di pareti circostanti il riempimento di supporto. L'aggiunta di una parete può rendere la stampa del supporto più affidabile ed in grado di supportare meglio gli sbalzi, ma aumenta il tempo di stampa ed il materiale utilizzato."
#: fdmprinter.def.json
msgctxt "zig_zaggify_support label"
@ -3714,9 +3710,7 @@ msgctxt "skirt_gap description"
msgid ""
"The horizontal distance between the skirt and the first layer of the print.\n"
"This is the minimum distance. Multiple skirt lines will extend outwards from this distance."
msgstr ""
"Indica la distanza orizzontale tra lo skirt ed il primo strato della stampa.\n"
"Questa è la distanza minima. Più linee di skirt aumenteranno tale distanza."
msgstr "Indica la distanza orizzontale tra lo skirt ed il primo strato della stampa.\nQuesta è la distanza minima. Più linee di skirt aumenteranno tale distanza."
#: fdmprinter.def.json
msgctxt "skirt_brim_minimal_length label"
@ -4676,12 +4670,12 @@ msgstr "La dimensione minima di un segmento di linea dopo il sezionamento. Se ta
#: fdmprinter.def.json
msgctxt "meshfix_maximum_travel_resolution label"
msgid "Maximum Travel Resolution"
msgstr ""
msgstr "Risoluzione massima di spostamento"
#: fdmprinter.def.json
msgctxt "meshfix_maximum_travel_resolution description"
msgid "The minimum size of a travel line segment after slicing. If you increase this, the travel moves will have less smooth corners. This may allow the printer to keep up with the speed it has to process g-code, but it may cause model avoidance to become less accurate."
msgstr ""
msgstr "La dimensione minima di un segmento lineare di spostamento dopo il sezionamento. Aumentando tale dimensione, le corse di spostamento avranno meno angoli arrotondati. La stampante può così mantenere la velocità per processare il g-code, ma si può verificare una riduzione della precisione di aggiramento del modello."
#: fdmprinter.def.json
msgctxt "support_skip_some_zags label"
@ -4846,22 +4840,22 @@ msgstr "Dimensioni delle cavità negli incroci a quattro vie nella configurazion
#: fdmprinter.def.json
msgctxt "cross_infill_density_image label"
msgid "Cross Infill Density Image"
msgstr ""
msgstr "Immagine di densità del riempimento incrociato"
#: fdmprinter.def.json
msgctxt "cross_infill_density_image description"
msgid "The file location of an image of which the brightness values determine the minimal density at the corresponding location in the infill of the print."
msgstr ""
msgstr "La posizione del file di un'immagine i cui i valori di luminosità determinano la densità minima nella posizione corrispondente nel riempimento della stampa."
#: fdmprinter.def.json
msgctxt "cross_support_density_image label"
msgid "Cross Fill Density Image for Support"
msgstr ""
msgstr "Immagine di densità del riempimento incrociato per il supporto"
#: fdmprinter.def.json
msgctxt "cross_support_density_image description"
msgid "The file location of an image of which the brightness values determine the minimal density at the corresponding location in the support."
msgstr ""
msgstr "La posizione del file di un'immagine i cui i valori di luminosità determinano la densità minima nella posizione corrispondente nel supporto."
#: fdmprinter.def.json
msgctxt "spaghetti_infill_enabled label"
@ -5173,9 +5167,7 @@ msgctxt "wireframe_up_half_speed description"
msgid ""
"Distance of an upward move which is extruded with half speed.\n"
"This can cause better adhesion to previous layers, while not heating the material in those layers too much. Only applies to Wire Printing."
msgstr ""
"Indica la distanza di uno spostamento verso l'alto con estrusione a velocità dimezzata.\n"
"Ciò può garantire una migliore adesione agli strati precedenti, senza eccessivo riscaldamento del materiale su questi strati. Applicabile solo alla funzione Wire Printing."
msgstr "Indica la distanza di uno spostamento verso l'alto con estrusione a velocità dimezzata.\nCiò può garantire una migliore adesione agli strati precedenti, senza eccessivo riscaldamento del materiale su questi strati. Applicabile solo alla funzione Wire Printing."
#: fdmprinter.def.json
msgctxt "wireframe_top_jump label"
@ -5300,7 +5292,7 @@ msgstr "Variazione massima strati adattivi"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation description"
msgid "The maximum allowed height different from the base layer height."
msgstr ""
msgstr "La differenza di altezza massima rispetto allaltezza dello strato di base."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step label"
@ -5535,7 +5527,7 @@ msgstr "Impostazioni utilizzate solo se CuraEngine non è chiamato dalla parte a
#: fdmprinter.def.json
msgctxt "center_object label"
msgid "Center Object"
msgstr ""
msgstr "Centra oggetto"
#: fdmprinter.def.json
msgctxt "center_object description"
@ -5545,27 +5537,27 @@ msgstr "Per centrare loggetto al centro del piano di stampa (0,0) anziché ut
#: fdmprinter.def.json
msgctxt "mesh_position_x label"
msgid "Mesh Position X"
msgstr ""
msgstr "Posizione maglia X"
#: fdmprinter.def.json
msgctxt "mesh_position_x description"
msgid "Offset applied to the object in the x direction."
msgstr "Offset applicato alloggetto per la direzione x."
msgstr "Offset applicato alloggetto per la direzione X."
#: fdmprinter.def.json
msgctxt "mesh_position_y label"
msgid "Mesh Position Y"
msgstr ""
msgstr "Posizione maglia Y"
#: fdmprinter.def.json
msgctxt "mesh_position_y description"
msgid "Offset applied to the object in the y direction."
msgstr "Offset applicato alloggetto per la direzione y."
msgstr "Offset applicato alloggetto per la direzione Y."
#: fdmprinter.def.json
msgctxt "mesh_position_z label"
msgid "Mesh Position Z"
msgstr ""
msgstr "Posizione maglia Z"
#: fdmprinter.def.json
msgctxt "mesh_position_z description"

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