diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000000..d25d71bcc9
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,4 @@
+.git
+.github
+resources/materials
+CuraEngine
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index b78b9b91a2..c69cf91433 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,9 +1,12 @@
@@ -23,6 +26,9 @@ Thank you for using Cura!
**Display Driver**
+**Printer**
+
+
**Steps to Reproduce**
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000..68255c56b9
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,45 @@
+FROM ultimaker/cura-build-environment:1
+
+# Environment vars for easy configuration
+ENV CURA_APP_DIR=/srv/cura
+
+# Ensure our sources dir exists
+RUN mkdir $CURA_APP_DIR
+
+# Setup CuraEngine
+ENV CURA_ENGINE_BRANCH=master
+WORKDIR $CURA_APP_DIR
+RUN git clone -b $CURA_ENGINE_BRANCH --depth 1 https://github.com/Ultimaker/CuraEngine
+WORKDIR $CURA_APP_DIR/CuraEngine
+RUN mkdir build
+WORKDIR $CURA_APP_DIR/CuraEngine/build
+RUN cmake3 ..
+RUN make
+RUN make install
+
+# TODO: setup libCharon
+
+# Setup Uranium
+ENV URANIUM_BRANCH=master
+WORKDIR $CURA_APP_DIR
+RUN git clone -b $URANIUM_BRANCH --depth 1 https://github.com/Ultimaker/Uranium
+
+# Setup materials
+ENV MATERIALS_BRANCH=master
+WORKDIR $CURA_APP_DIR
+RUN git clone -b $MATERIALS_BRANCH --depth 1 https://github.com/Ultimaker/fdm_materials materials
+
+# Setup Cura
+WORKDIR $CURA_APP_DIR/Cura
+ADD . .
+RUN mv $CURA_APP_DIR/materials resources/materials
+
+# Make sure Cura can find CuraEngine
+RUN ln -s /usr/local/bin/CuraEngine $CURA_APP_DIR/Cura
+
+# Run Cura
+WORKDIR $CURA_APP_DIR/Cura
+ENV PYTHONPATH=${PYTHONPATH}:$CURA_APP_DIR/Uranium
+RUN chmod +x ./CuraEngine
+RUN chmod +x ./run_in_docker.sh
+CMD "./run_in_docker.sh"
diff --git a/README.md b/README.md
index 366739e4be..70466e9c22 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,10 @@ Build scripts
-------------
Please checkout [cura-build](https://github.com/Ultimaker/cura-build) for detailed building instructions.
+Running from Source
+-------------
+Please check our [Wiki page](https://github.com/Ultimaker/Cura/wiki/Running-Cura-from-Source) for details about running Cura from source.
+
Plugins
-------------
Please check our [Wiki page](https://github.com/Ultimaker/Cura/wiki/Plugin-Directory) for details about creating and using plugins.
diff --git a/cura.appdata.xml b/cura.appdata.xml
index 5c67814fae..2d8bff15ec 100644
--- a/cura.appdata.xml
+++ b/cura.appdata.xml
@@ -3,7 +3,7 @@
cura.desktopCC0-1.0
- AGPL-3.0 and CC-BY-SA-4.0
+ LGPL-3.0 and CC-BY-SA-4.0CuraThe world's most advanced 3d printer software
@@ -15,7 +15,7 @@
Novices can start printing right away
-
Experts are able to customize 200 settings to achieve the best results
+
Experts are able to customize 300 settings to achieve the best results
Optimized profiles for Ultimaker materials
Supported by a global network of Ultimaker certified service partners
Print multiple objects at once with different settings for each object
@@ -26,6 +26,6 @@
http://software.ultimaker.com/Cura.png
- https://ultimaker.com/en/products/cura-software
+ https://ultimaker.com/en/products/cura-software?utm_source=cura&utm_medium=software&utm_campaign=resourcesCura
diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py
index 7a3b5ced63..6da8f33fff 100755
--- a/cura/BuildVolume.py
+++ b/cura/BuildVolume.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2017 Ultimaker B.V.
+# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from cura.Scene.CuraSceneNode import CuraSceneNode
@@ -74,6 +74,11 @@ class BuildVolume(SceneNode):
self._adhesion_type = None
self._platform = Platform(self)
+ self._build_volume_message = Message(catalog.i18nc("@info:status",
+ "The build volume height has been reduced due to the value of the"
+ " \"Print Sequence\" setting to prevent the gantry from colliding"
+ " with printed models."), title = catalog.i18nc("@info:title", "Build Volume"))
+
self._global_container_stack = None
Application.getInstance().globalContainerStackChanged.connect(self._onStackChanged)
self._onStackChanged()
@@ -97,11 +102,6 @@ class BuildVolume(SceneNode):
self._setting_change_timer.setSingleShot(True)
self._setting_change_timer.timeout.connect(self._onSettingChangeTimerFinished)
- self._build_volume_message = Message(catalog.i18nc("@info:status",
- "The build volume height has been reduced due to the value of the"
- " \"Print Sequence\" setting to prevent the gantry from colliding"
- " with printed models."), title = catalog.i18nc("@info:title","Build Volume"))
-
# Must be after setting _build_volume_message, apparently that is used in getMachineManager.
# activeQualityChanged is always emitted after setActiveVariant, setActiveMaterial and setActiveQuality.
# Therefore this works.
@@ -111,6 +111,9 @@ class BuildVolume(SceneNode):
# but it does not update the disallowed areas after material change
Application.getInstance().getMachineManager().activeStackChanged.connect(self._onStackChanged)
+ # Enable and disable extruder
+ Application.getInstance().getMachineManager().extruderChanged.connect(self.updateNodeBoundaryCheck)
+
# list of settings which were updated
self._changed_settings_since_last_rebuild = []
@@ -133,6 +136,7 @@ class BuildVolume(SceneNode):
if active_extruder_changed is not None:
node.callDecoration("getActiveExtruderChangedSignal").disconnect(self._updateDisallowedAreasAndRebuild)
node.decoratorsChanged.disconnect(self._updateNodeListeners)
+ self._updateDisallowedAreasAndRebuild() # make sure we didn't miss anything before we updated the node listeners
self._scene_objects = new_scene_objects
self._onSettingPropertyChanged("print_sequence", "value") # Create fake event, so right settings are triggered.
@@ -147,7 +151,6 @@ class BuildVolume(SceneNode):
active_extruder_changed = node.callDecoration("getActiveExtruderChangedSignal")
if active_extruder_changed is not None:
active_extruder_changed.connect(self._updateDisallowedAreasAndRebuild)
- self._updateDisallowedAreasAndRebuild()
def setWidth(self, width):
if width is not None:
@@ -217,30 +220,35 @@ class BuildVolume(SceneNode):
group_nodes.append(node) # Keep list of affected group_nodes
if node.callDecoration("isSliceable") or node.callDecoration("isGroup"):
- node._outside_buildarea = False
- bbox = node.getBoundingBox()
-
- # Mark the node as outside the build volume if the bounding box test fails.
- if build_volume_bounding_box.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
- node._outside_buildarea = True
+ if node.collidesWithBbox(build_volume_bounding_box):
+ node.setOutsideBuildArea(True)
continue
- convex_hull = node.callDecoration("getConvexHull")
- if convex_hull:
- if not convex_hull.isValid():
- return
- # Check for collisions between disallowed areas and the object
- for area in self.getDisallowedAreas():
- overlap = convex_hull.intersectsPolygon(area)
- if overlap is None:
- continue
- node._outside_buildarea = True
- continue
+ if node.collidesWithArea(self.getDisallowedAreas()):
+ node.setOutsideBuildArea(True)
+ continue
+
+ # Mark the node as outside build volume if the set extruder is disabled
+ extruder_position = node.callDecoration("getActiveExtruderPosition")
+ if not self._global_container_stack.extruders[extruder_position].isEnabled:
+ node.setOutsideBuildArea(True)
+ continue
+
+ node.setOutsideBuildArea(False)
# Group nodes should override the _outside_buildarea property of their children.
for group_node in group_nodes:
- for child_node in group_node.getAllChildren():
- child_node._outside_buildarea = group_node._outside_buildarea
+ children = group_node.getAllChildren()
+
+ # Check if one or more children are non-printable and if so, set the parent as non-printable:
+ for child_node in children:
+ if child_node.isOutsideBuildArea():
+ group_node.setOutsideBuildArea(True)
+ break
+
+ # Apply results of the check to all children of the group:
+ for child_node in children:
+ child_node.setOutsideBuildArea(group_node.isOutsideBuildArea())
## Update the outsideBuildArea of a single node, given bounds or current build volume
def checkBoundsAndUpdate(self, node: CuraSceneNode, bounds: Optional[AxisAlignedBox] = None):
@@ -260,24 +268,20 @@ class BuildVolume(SceneNode):
build_volume_bounding_box = bounds
if node.callDecoration("isSliceable") or node.callDecoration("isGroup"):
- bbox = node.getBoundingBox()
-
- # Mark the node as outside the build volume if the bounding box test fails.
- if build_volume_bounding_box.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
+ if node.collidesWithBbox(build_volume_bounding_box):
+ node.setOutsideBuildArea(True)
+ return
+
+ if node.collidesWithArea(self.getDisallowedAreas()):
+ node.setOutsideBuildArea(True)
+ return
+
+ # Mark the node as outside build volume if the set extruder is disabled
+ extruder_position = node.callDecoration("getActiveExtruderPosition")
+ if not self._global_container_stack.extruders[extruder_position].isEnabled:
node.setOutsideBuildArea(True)
return
- convex_hull = self.callDecoration("getConvexHull")
- if convex_hull:
- if not convex_hull.isValid():
- return
- # Check for collisions between disallowed areas and the object
- for area in self.getDisallowedAreas():
- overlap = convex_hull.intersectsPolygon(area)
- if overlap is None:
- continue
- node.setOutsideBuildArea(True)
- return
node.setOutsideBuildArea(False)
## Recalculates the build volume & disallowed areas.
@@ -737,12 +741,17 @@ class BuildVolume(SceneNode):
prime_tower_x = prime_tower_x - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
prime_tower_y = prime_tower_y + machine_depth / 2
- prime_tower_area = Polygon([
- [prime_tower_x - prime_tower_size, prime_tower_y - prime_tower_size],
- [prime_tower_x, prime_tower_y - prime_tower_size],
- [prime_tower_x, prime_tower_y],
- [prime_tower_x - prime_tower_size, prime_tower_y],
- ])
+ if self._global_container_stack.getProperty("prime_tower_circular", "value"):
+ radius = prime_tower_size / 2
+ prime_tower_area = Polygon.approximatedCircle(radius)
+ prime_tower_area = prime_tower_area.translate(prime_tower_x - radius, prime_tower_y - radius)
+ else:
+ prime_tower_area = Polygon([
+ [prime_tower_x - prime_tower_size, prime_tower_y - prime_tower_size],
+ [prime_tower_x, prime_tower_y - prime_tower_size],
+ [prime_tower_x, prime_tower_y],
+ [prime_tower_x - prime_tower_size, prime_tower_y],
+ ])
prime_tower_area = prime_tower_area.getMinkowskiHull(Polygon.approximatedCircle(0))
for extruder in used_extruders:
result[extruder.getId()].append(prime_tower_area) #The prime tower location is the same for each extruder, regardless of offset.
@@ -821,6 +830,7 @@ class BuildVolume(SceneNode):
offset_y = extruder.getProperty("machine_nozzle_offset_y", "value")
if offset_y is None:
offset_y = 0
+ offset_y = -offset_y #Y direction of g-code is the inverse of Y direction of Cura's scene space.
result[extruder_id] = []
for polygon in machine_disallowed_polygons:
@@ -931,8 +941,8 @@ class BuildVolume(SceneNode):
# stack.
#
# \return A sequence of setting values, one for each extruder.
- def _getSettingFromAllExtruders(self, setting_key, property = "value"):
- all_values = ExtruderManager.getInstance().getAllExtruderSettings(setting_key, property)
+ def _getSettingFromAllExtruders(self, setting_key):
+ all_values = ExtruderManager.getInstance().getAllExtruderSettings(setting_key, "value")
all_types = ExtruderManager.getInstance().getAllExtruderSettings(setting_key, "type")
for i in range(len(all_values)):
if not all_values[i] and (all_types[i] == "int" or all_types[i] == "float"):
@@ -945,7 +955,7 @@ class BuildVolume(SceneNode):
# not part of the collision radius, such as bed adhesion (skirt/brim/raft)
# and travel avoid distance.
def _getEdgeDisallowedSize(self):
- if not self._global_container_stack:
+ if not self._global_container_stack or not self._global_container_stack.extruders:
return 0
container_stack = self._global_container_stack
@@ -1023,7 +1033,7 @@ class BuildVolume(SceneNode):
_raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap", "layer_0_z_overlap"]
_extra_z_settings = ["retraction_hop_enabled", "retraction_hop"]
_prime_settings = ["extruder_prime_pos_x", "extruder_prime_pos_y", "extruder_prime_pos_z", "prime_blob_enable"]
- _tower_settings = ["prime_tower_enable", "prime_tower_size", "prime_tower_position_x", "prime_tower_position_y"]
+ _tower_settings = ["prime_tower_enable", "prime_tower_circular", "prime_tower_size", "prime_tower_position_x", "prime_tower_position_y"]
_ooze_shield_settings = ["ooze_shield_enabled", "ooze_shield_dist"]
_distance_settings = ["infill_wipe_dist", "travel_avoid_distance", "support_offset", "support_enable", "travel_avoid_other_parts"]
_extruder_settings = ["support_enable", "support_bottom_enable", "support_roof_enable", "support_infill_extruder_nr", "support_extruder_nr_layer_0", "support_bottom_extruder_nr", "support_roof_extruder_nr", "brim_line_count", "adhesion_extruder_nr", "adhesion_type"] #Settings that can affect which extruders are used.
diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py
index 0c6740f740..7700ee2e71 100644
--- a/cura/CrashHandler.py
+++ b/cura/CrashHandler.py
@@ -1,7 +1,6 @@
-# Copyright (c) 2017 Ultimaker B.V.
+# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
-import sys
import platform
import traceback
import faulthandler
@@ -13,15 +12,19 @@ import json
import ssl
import urllib.request
import urllib.error
+import shutil
-from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QCoreApplication
-from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit, QGroupBox
+from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, Qt, QUrl
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit, QGroupBox, QCheckBox, QPushButton
+from PyQt5.QtGui import QDesktopServices
+from UM.Resources import Resources
from UM.Application import Application
from UM.Logger import Logger
from UM.View.GL.OpenGL import OpenGL
from UM.i18n import i18nCatalog
from UM.Platform import Platform
+from UM.Resources import Resources
catalog = i18nCatalog("cura")
@@ -49,10 +52,11 @@ fatal_exception_types = [
class CrashHandler:
crash_url = "https://stats.ultimaker.com/api/cura"
- def __init__(self, exception_type, value, tb):
+ def __init__(self, exception_type, value, tb, has_started = True):
self.exception_type = exception_type
self.value = value
self.traceback = tb
+ self.has_started = has_started
self.dialog = None # Don't create a QDialog before there is a QApplication
# While we create the GUI, the information will be stored for sending afterwards
@@ -64,21 +68,143 @@ class CrashHandler:
for part in line.rstrip("\n").split("\n"):
Logger.log("c", part)
- if not CuraDebugMode and exception_type not in fatal_exception_types:
+ # If Cura has fully started, we only show fatal errors.
+ # If Cura has not fully started yet, we always show the early crash dialog. Otherwise, Cura will just crash
+ # without any information.
+ if has_started and exception_type not in fatal_exception_types:
return
- application = QCoreApplication.instance()
- if not application:
- sys.exit(1)
+ if not has_started:
+ self._send_report_checkbox = None
+ self.early_crash_dialog = self._createEarlyCrashDialog()
self.dialog = QDialog()
self._createDialog()
+ def _createEarlyCrashDialog(self):
+ dialog = QDialog()
+ dialog.setMinimumWidth(500)
+ dialog.setMinimumHeight(170)
+ dialog.setWindowTitle(catalog.i18nc("@title:window", "Cura can't start"))
+ dialog.finished.connect(self._closeEarlyCrashDialog)
+
+ layout = QVBoxLayout(dialog)
+
+ label = QLabel()
+ label.setText(catalog.i18nc("@label crash message", """
Oops, Ultimaker Cura has encountered something that doesn't seem right.
+
We encountered an unrecoverable error during start up. It was possibly caused by some incorrect configuration files. We suggest to backup and reset your configuration.
+
Backups can be found in the configuration folder.
+
Please send us this Crash Report to fix the problem.
+ """))
+ label.setWordWrap(True)
+ layout.addWidget(label)
+
+ # "send report" check box and show details
+ self._send_report_checkbox = QCheckBox(catalog.i18nc("@action:button", "Send crash report to Ultimaker"), dialog)
+ self._send_report_checkbox.setChecked(True)
+
+ show_details_button = QPushButton(catalog.i18nc("@action:button", "Show detailed crash report"), dialog)
+ show_details_button.setMaximumWidth(200)
+ show_details_button.clicked.connect(self._showDetailedReport)
+
+ show_configuration_folder_button = QPushButton(catalog.i18nc("@action:button", "Show configuration folder"), dialog)
+ show_configuration_folder_button.setMaximumWidth(200)
+ show_configuration_folder_button.clicked.connect(self._showConfigurationFolder)
+
+ layout.addWidget(self._send_report_checkbox)
+ layout.addWidget(show_details_button)
+ layout.addWidget(show_configuration_folder_button)
+
+ # "backup and start clean" and "close" buttons
+ buttons = QDialogButtonBox()
+ buttons.addButton(QDialogButtonBox.Close)
+ buttons.addButton(catalog.i18nc("@action:button", "Backup and Reset Configuration"), QDialogButtonBox.AcceptRole)
+ buttons.rejected.connect(self._closeEarlyCrashDialog)
+ buttons.accepted.connect(self._backupAndStartClean)
+
+ layout.addWidget(buttons)
+
+ return dialog
+
+ def _closeEarlyCrashDialog(self):
+ if self._send_report_checkbox.isChecked():
+ self._sendCrashReport()
+ os._exit(1)
+
+ def _backupAndStartClean(self):
+ # backup the current cura directories and create clean ones
+ from cura.CuraVersion import CuraVersion
+ from UM.Resources import Resources
+ # The early crash may happen before those information is set in Resources, so we need to set them here to
+ # make sure that Resources can find the correct place.
+ Resources.ApplicationIdentifier = "cura"
+ Resources.ApplicationVersion = CuraVersion
+ config_path = Resources.getConfigStoragePath()
+ data_path = Resources.getDataStoragePath()
+ cache_path = Resources.getCacheStoragePath()
+
+ folders_to_backup = []
+ folders_to_remove = [] # only cache folder needs to be removed
+
+ folders_to_backup.append(config_path)
+ if data_path != config_path:
+ folders_to_backup.append(data_path)
+
+ # Only remove the cache folder if it's not the same as data or config
+ if cache_path not in (config_path, data_path):
+ folders_to_remove.append(cache_path)
+
+ for folder in folders_to_remove:
+ shutil.rmtree(folder, ignore_errors = True)
+ for folder in folders_to_backup:
+ base_name = os.path.basename(folder)
+ root_dir = os.path.dirname(folder)
+
+ import datetime
+ date_now = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
+ idx = 0
+ file_name = base_name + "_" + date_now
+ zip_file_path = os.path.join(root_dir, file_name + ".zip")
+ while os.path.exists(zip_file_path):
+ idx += 1
+ file_name = base_name + "_" + date_now + "_" + idx
+ zip_file_path = os.path.join(root_dir, file_name + ".zip")
+ try:
+ # only create the zip backup when the folder exists
+ if os.path.exists(folder):
+ # remove the .zip extension because make_archive() adds it
+ zip_file_path = zip_file_path[:-4]
+ shutil.make_archive(zip_file_path, "zip", root_dir = root_dir, base_dir = base_name)
+
+ # remove the folder only when the backup is successful
+ shutil.rmtree(folder, ignore_errors = True)
+
+ # create an empty folder so Resources will not try to copy the old ones
+ os.makedirs(folder, 0o0755, exist_ok=True)
+
+ except Exception as e:
+ Logger.logException("e", "Failed to backup [%s] to file [%s]", folder, zip_file_path)
+ if not self.has_started:
+ print("Failed to backup [%s] to file [%s]: %s", folder, zip_file_path, e)
+
+ self.early_crash_dialog.close()
+
+ def _showConfigurationFolder(self):
+ path = Resources.getConfigStoragePath();
+ QDesktopServices.openUrl(QUrl.fromLocalFile( path ))
+
+ def _showDetailedReport(self):
+ self.dialog.exec_()
+
## Creates a modal dialog.
def _createDialog(self):
self.dialog.setMinimumWidth(640)
self.dialog.setMinimumHeight(640)
self.dialog.setWindowTitle(catalog.i18nc("@title:window", "Crash Report"))
+ # if the application has not fully started, this will be a detailed report dialog which should not
+ # close the application when it's closed.
+ if self.has_started:
+ self.dialog.finished.connect(self._close)
layout = QVBoxLayout(self.dialog)
@@ -89,9 +215,12 @@ class CrashHandler:
layout.addWidget(self._userDescriptionWidget())
layout.addWidget(self._buttonsWidget())
+ def _close(self):
+ os._exit(1)
+
def _messageWidget(self):
label = QLabel()
- label.setText(catalog.i18nc("@label crash message", """
A fatal error has occurred. Please send us this Crash Report to fix the problem
A fatal error has occurred in Cura. Please send us this Crash Report to fix the problem
Please use the "Send report" button to post a bug report automatically to our servers
"""))
@@ -130,7 +259,7 @@ class CrashHandler:
opengl_instance = OpenGL.getInstance()
if not opengl_instance:
self.data["opengl"] = {"version": "n/a", "vendor": "n/a", "type": "n/a"}
- return catalog.i18nc("@label", "not yet initialised ")
+ return catalog.i18nc("@label", "Not yet initialized ")
info = "
"
info += catalog.i18nc("@label OpenGL version", "
OpenGL Version: {version}
").format(version = opengl_instance.getOpenGLVersion())
@@ -148,8 +277,8 @@ class CrashHandler:
layout = QVBoxLayout()
text_area = QTextEdit()
- trace_dict = traceback.format_exception(self.exception_type, self.value, self.traceback)
- trace = "".join(trace_dict)
+ trace_list = traceback.format_exception(self.exception_type, self.value, self.traceback)
+ trace = "".join(trace_list)
text_area.setText(trace)
text_area.setReadOnly(True)
@@ -157,14 +286,28 @@ class CrashHandler:
group.setLayout(layout)
# Parsing all the information to fill the dictionary
- summary = trace_dict[len(trace_dict)-1].rstrip("\n")
- module = trace_dict[len(trace_dict)-2].rstrip("\n").split("\n")
+ summary = ""
+ if len(trace_list) >= 1:
+ summary = trace_list[len(trace_list)-1].rstrip("\n")
+ module = [""]
+ if len(trace_list) >= 2:
+ module = trace_list[len(trace_list)-2].rstrip("\n").split("\n")
module_split = module[0].split(", ")
- filepath = module_split[0].split("\"")[1]
+
+ filepath_directory_split = module_split[0].split("\"")
+ filepath = ""
+ if len(filepath_directory_split) > 1:
+ filepath = filepath_directory_split[1]
directory, filename = os.path.split(filepath)
- line = int(module_split[1].lstrip("line "))
- function = module_split[2].lstrip("in ")
- code = module[1].lstrip(" ")
+ line = ""
+ if len(module_split) > 1:
+ line = int(module_split[1].lstrip("line "))
+ function = ""
+ if len(module_split) > 2:
+ function = module_split[2].lstrip("in ")
+ code = ""
+ if len(module) > 1:
+ code = module[1].lstrip(" ")
# Using this workaround for a cross-platform path splitting
split_path = []
@@ -249,9 +392,13 @@ class CrashHandler:
def _buttonsWidget(self):
buttons = QDialogButtonBox()
buttons.addButton(QDialogButtonBox.Close)
- buttons.addButton(catalog.i18nc("@action:button", "Send report"), QDialogButtonBox.AcceptRole)
+ # Like above, this will be served as a separate detailed report dialog if the application has not yet been
+ # fully loaded. In this case, "send report" will be a check box in the early crash dialog, so there is no
+ # need for this extra button.
+ if self.has_started:
+ buttons.addButton(catalog.i18nc("@action:button", "Send report"), QDialogButtonBox.AcceptRole)
+ buttons.accepted.connect(self._sendCrashReport)
buttons.rejected.connect(self.dialog.close)
- buttons.accepted.connect(self._sendCrashReport)
return buttons
@@ -269,15 +416,23 @@ class CrashHandler:
kwoptions["context"] = ssl._create_unverified_context()
Logger.log("i", "Sending crash report info to [%s]...", self.crash_url)
+ if not self.has_started:
+ print("Sending crash report info to [%s]...\n" % self.crash_url)
try:
f = urllib.request.urlopen(self.crash_url, **kwoptions)
Logger.log("i", "Sent crash report info.")
+ if not self.has_started:
+ print("Sent crash report info.\n")
f.close()
- except urllib.error.HTTPError:
+ except urllib.error.HTTPError as e:
Logger.logException("e", "An HTTP error occurred while trying to send crash report")
- except Exception: # We don't want any exception to cause problems
+ if not self.has_started:
+ print("An HTTP error occurred while trying to send crash report: %s" % e)
+ except Exception as e: # We don't want any exception to cause problems
Logger.logException("e", "An exception occurred while trying to send crash report")
+ if not self.has_started:
+ print("An exception occurred while trying to send crash report: %s" % e)
os._exit(1)
diff --git a/cura/CuraActions.py b/cura/CuraActions.py
index f517ec4217..75338f17b6 100644
--- a/cura/CuraActions.py
+++ b/cura/CuraActions.py
@@ -109,10 +109,6 @@ class CuraActions(QObject):
nodes_to_change = []
for node in Selection.getAllSelectedObjects():
- # Do not change any nodes that already have the right extruder set.
- if node.callDecoration("getActiveExtruder") == extruder_id:
- continue
-
# 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):
@@ -125,6 +121,10 @@ class CuraActions(QObject):
nodes_to_change.append(grouped_node)
continue
+ # Do not change any nodes that already have the right extruder set.
+ if node.callDecoration("getActiveExtruder") == extruder_id:
+ continue
+
nodes_to_change.append(node)
if not nodes_to_change:
diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py
index c58f62869c..292d0bce94 100755
--- a/cura/CuraApplication.py
+++ b/cura/CuraApplication.py
@@ -1,6 +1,7 @@
-# Copyright (c) 2017 Ultimaker B.V.
-# 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, QTimer
from PyQt5.QtNetwork import QLocalServer
from PyQt5.QtNetwork import QLocalSocket
@@ -11,6 +12,7 @@ from UM.Math.Vector import Vector
from UM.Math.Quaternion import Quaternion
from UM.Math.AxisAlignedBox import AxisAlignedBox
from UM.Math.Matrix import Matrix
+from UM.Platform import Platform
from UM.Resources import Resources
from UM.Scene.ToolHandle import ToolHandle
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
@@ -50,13 +52,27 @@ from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyT
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.SettingFunction import SettingFunction
from cura.Settings.MachineNameValidator import MachineNameValidator
-from cura.Settings.ProfilesModel import ProfilesModel
-from cura.Settings.MaterialsModel import MaterialsModel
-from cura.Settings.QualityAndUserProfilesModel import QualityAndUserProfilesModel
+
+from cura.Machines.Models.BuildPlateModel import BuildPlateModel
+from cura.Machines.Models.NozzleModel import NozzleModel
+from cura.Machines.Models.QualityProfilesDropDownMenuModel import QualityProfilesDropDownMenuModel
+from cura.Machines.Models.CustomQualityProfilesDropDownMenuModel import CustomQualityProfilesDropDownMenuModel
+from cura.Machines.Models.MultiBuildPlateModel import MultiBuildPlateModel
+from cura.Machines.Models.MaterialManagementModel import MaterialManagementModel
+from cura.Machines.Models.GenericMaterialsModel import GenericMaterialsModel
+from cura.Machines.Models.BrandMaterialsModel import BrandMaterialsModel
+from cura.Machines.Models.QualityManagementModel import QualityManagementModel
+from cura.Machines.Models.QualitySettingsModel import QualitySettingsModel
+from cura.Machines.Models.MachineManagementModel import MachineManagementModel
+
+from cura.Machines.Models.SettingVisibilityPresetsModel import SettingVisibilityPresetsModel
+
+from cura.Machines.MachineErrorChecker import MachineErrorChecker
+
from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
-from cura.Settings.UserProfilesModel import UserProfilesModel
from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
+from cura.Machines.VariantManager import VariantManager
from . import PlatformPhysics
from . import BuildVolume
@@ -69,17 +85,13 @@ from . import CameraImageProvider
from . import MachineActionManager
from cura.Settings.MachineManager import MachineManager
-from cura.Settings.MaterialManager import MaterialManager
from cura.Settings.ExtruderManager import ExtruderManager
from cura.Settings.UserChangesModel import UserChangesModel
from cura.Settings.ExtrudersModel import ExtrudersModel
-from cura.Settings.ContainerSettingsModel import ContainerSettingsModel
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
-from cura.Settings.QualitySettingsModel import QualitySettingsModel
from cura.Settings.ContainerManager import ContainerManager
from cura.ObjectsModel import ObjectsModel
-from cura.BuildPlateModel import BuildPlateModel
from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
from UM.FlameProfiler import pyqtSlot
@@ -95,6 +107,7 @@ import os
import argparse
import json
+
numpy.seterr(all="ignore")
MYPY = False
@@ -113,6 +126,8 @@ class CuraApplication(QtApplication):
# changes of the settings.
SettingVersion = 4
+ Created = False
+
class ResourceTypes:
QmlFiles = Resources.UserType + 1
Firmware = Resources.UserType + 2
@@ -123,17 +138,11 @@ class CuraApplication(QtApplication):
MachineStack = Resources.UserType + 7
ExtruderStack = Resources.UserType + 8
DefinitionChangesContainer = Resources.UserType + 9
+ SettingVisibilityPreset = Resources.UserType + 10
Q_ENUMS(ResourceTypes)
- # FIXME: This signal belongs to the MachineManager, but the CuraEngineBackend plugin requires on it.
- # Because plugins are initialized before the ContainerRegistry, putting this signal in MachineManager
- # will make it initialized before ContainerRegistry does, and it won't find the active machine, thus
- # Cura will always show the Add Machine Dialog upon start.
- stacksValidationFinished = pyqtSignal() # Emitted whenever a validation is finished
-
def __init__(self, **kwargs):
-
# this list of dir names will be used by UM to detect an old cura directory
for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "user", "variants"]:
Resources.addExpectedDirNameInData(dir_name)
@@ -177,6 +186,7 @@ class CuraApplication(QtApplication):
Resources.addStorageType(self.ResourceTypes.ExtruderStack, "extruders")
Resources.addStorageType(self.ResourceTypes.MachineStack, "machine_instances")
Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")
+ Resources.addStorageType(self.ResourceTypes.SettingVisibilityPreset, "setting_visibility")
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality")
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality_changes")
@@ -194,10 +204,10 @@ class CuraApplication(QtApplication):
UM.VersionUpgradeManager.VersionUpgradeManager.getInstance().setCurrentVersions(
{
("quality_changes", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.QualityInstanceContainer, "application/x-uranium-instancecontainer"),
- ("machine_stack", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.MachineStack, "application/x-cura-globalstack"),
- ("extruder_train", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.ExtruderStack, "application/x-cura-extruderstack"),
- ("preferences", Preferences.Version * 1000000 + self.SettingVersion): (Resources.Preferences, "application/x-uranium-preferences"),
- ("user", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.UserInstanceContainer, "application/x-uranium-instancecontainer"),
+ ("machine_stack", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.MachineStack, "application/x-cura-globalstack"),
+ ("extruder_train", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.ExtruderStack, "application/x-cura-extruderstack"),
+ ("preferences", Preferences.Version * 1000000 + self.SettingVersion): (Resources.Preferences, "application/x-uranium-preferences"),
+ ("user", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.UserInstanceContainer, "application/x-uranium-instancecontainer"),
("definition_changes", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.DefinitionChangesContainer, "application/x-uranium-instancecontainer"),
}
)
@@ -209,11 +219,15 @@ class CuraApplication(QtApplication):
self._machine_manager = None # This is initialized on demand.
self._extruder_manager = None
self._material_manager = None
+ self._quality_manager = None
self._object_manager = None
self._build_plate_model = None
+ self._multi_build_plate_model = None
+ self._setting_visibility_presets_model = None
self._setting_inheritance_manager = None
self._simple_mode_settings_manager = None
self._cura_scene_controller = None
+ self._machine_error_checker = None
self._additional_components = {} # Components to add to certain areas in the interface
@@ -224,6 +238,12 @@ class CuraApplication(QtApplication):
tray_icon_name = "cura-icon-32.png",
**kwargs)
+ # FOR TESTING ONLY
+ if kwargs["parsed_command_line"].get("trigger_early_crash", False):
+ assert not "This crash is triggered by the trigger_early_crash command line argument."
+
+ self._variant_manager = None
+
self.default_theme = "cura-light"
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
@@ -257,17 +277,22 @@ class CuraApplication(QtApplication):
self._center_after_select = False
self._camera_animation = None
self._cura_actions = None
- self._started = False
+ self.started = False
self._message_box_callback = None
self._message_box_callback_arguments = []
self._preferred_mimetype = ""
self._i18n_catalog = i18nCatalog("cura")
- self.getController().getScene().sceneChanged.connect(self.updatePlatformActivity)
+ self._update_platform_activity_timer = QTimer()
+ self._update_platform_activity_timer.setInterval(500)
+ self._update_platform_activity_timer.setSingleShot(True)
+ self._update_platform_activity_timer.timeout.connect(self.updatePlatformActivity)
+
+ self.getController().getScene().sceneChanged.connect(self.updatePlatformActivityDelayed)
self.getController().toolOperationStopped.connect(self._onToolOperationStopped)
self.getController().contextMenuRequested.connect(self._onContextMenuRequested)
- self.getCuraSceneController().activeBuildPlateChanged.connect(self.updatePlatformActivity)
+ self.getCuraSceneController().activeBuildPlateChanged.connect(self.updatePlatformActivityDelayed)
Resources.addType(self.ResourceTypes.QmlFiles, "qml")
Resources.addType(self.ResourceTypes.Firmware, "firmware")
@@ -278,21 +303,25 @@ class CuraApplication(QtApplication):
# Since they are empty, they should never be serialized and instead just programmatically created.
# We need them to simplify the switching between materials.
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
+ self.empty_container = empty_container
empty_definition_changes_container = copy.deepcopy(empty_container)
empty_definition_changes_container.setMetaDataEntry("id", "empty_definition_changes")
empty_definition_changes_container.addMetaDataEntry("type", "definition_changes")
ContainerRegistry.getInstance().addContainer(empty_definition_changes_container)
+ self.empty_definition_changes_container = empty_definition_changes_container
empty_variant_container = copy.deepcopy(empty_container)
empty_variant_container.setMetaDataEntry("id", "empty_variant")
empty_variant_container.addMetaDataEntry("type", "variant")
ContainerRegistry.getInstance().addContainer(empty_variant_container)
+ self.empty_variant_container = empty_variant_container
empty_material_container = copy.deepcopy(empty_container)
empty_material_container.setMetaDataEntry("id", "empty_material")
empty_material_container.addMetaDataEntry("type", "material")
ContainerRegistry.getInstance().addContainer(empty_material_container)
+ self.empty_material_container = empty_material_container
empty_quality_container = copy.deepcopy(empty_container)
empty_quality_container.setMetaDataEntry("id", "empty_quality")
@@ -301,12 +330,14 @@ class CuraApplication(QtApplication):
empty_quality_container.addMetaDataEntry("type", "quality")
empty_quality_container.addMetaDataEntry("supported", False)
ContainerRegistry.getInstance().addContainer(empty_quality_container)
+ self.empty_quality_container = empty_quality_container
empty_quality_changes_container = copy.deepcopy(empty_container)
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
empty_quality_changes_container.addMetaDataEntry("type", "quality_changes")
empty_quality_changes_container.addMetaDataEntry("quality_type", "not_supported")
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
+ self.empty_quality_changes_container = empty_quality_changes_container
with ContainerRegistry.getInstance().lockFile():
ContainerRegistry.getInstance().loadAllMetadata()
@@ -348,58 +379,6 @@ class CuraApplication(QtApplication):
preferences.setDefault("local_file/last_used_type", "text/x-gcode")
- preferences.setDefault("general/visible_settings", """
- machine_settings
- resolution
- layer_height
- shell
- wall_thickness
- top_bottom_thickness
- z_seam_x
- z_seam_y
- infill
- infill_sparse_density
- gradual_infill_steps
- material
- material_print_temperature
- material_bed_temperature
- material_diameter
- material_flow
- retraction_enable
- speed
- speed_print
- speed_travel
- acceleration_print
- acceleration_travel
- jerk_print
- jerk_travel
- travel
- cooling
- cool_fan_enabled
- support
- support_enable
- support_extruder_nr
- support_type
- platform_adhesion
- adhesion_type
- adhesion_extruder_nr
- brim_width
- raft_airgap
- layer_0_z_overlap
- raft_surface_layers
- dual
- prime_tower_enable
- prime_tower_size
- prime_tower_position_x
- prime_tower_position_y
- meshfix
- blackmagic
- print_sequence
- infill_mesh
- cutting_mesh
- experimental
- """.replace("\n", ";").replace(" ", ""))
-
self.applicationShuttingDown.connect(self.saveSettings)
self.engineCreatedSignal.connect(self._onEngineCreated)
@@ -410,6 +389,12 @@ class CuraApplication(QtApplication):
self.getCuraSceneController().setActiveBuildPlate(0) # Initialize
+ self._quality_profile_drop_down_menu_model = None
+ self._custom_quality_profile_drop_down_menu_model = None
+
+ CuraApplication.Created = True
+
+
def _onEngineCreated(self):
self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
@@ -464,32 +449,20 @@ class CuraApplication(QtApplication):
has_user_interaction = True
return has_user_interaction
- onDiscardOrKeepProfileChangesClosed = pyqtSignal() # Used to notify other managers that the dialog was closed
-
@pyqtSlot(str)
def discardOrKeepProfileChangesClosed(self, option):
+ global_stack = self.getGlobalContainerStack()
if option == "discard":
- global_stack = self.getGlobalContainerStack()
- for extruder in self._extruder_manager.getMachineExtruders(global_stack.getId()):
- extruder.getTop().clear()
- global_stack.getTop().clear()
+ for extruder in global_stack.extruders.values():
+ extruder.userChanges.clear()
+ global_stack.userChanges.clear()
# if the user decided to keep settings then the user settings should be re-calculated and validated for errors
# before slicing. To ensure that slicer uses right settings values
elif option == "keep":
- global_stack = self.getGlobalContainerStack()
- for extruder in self._extruder_manager.getMachineExtruders(global_stack.getId()):
- user_extruder_container = extruder.getTop()
- if user_extruder_container:
- user_extruder_container.update()
-
- user_global_container = global_stack.getTop()
- if user_global_container:
- user_global_container.update()
-
- # notify listeners that quality has changed (after user selected discard or keep)
- self.onDiscardOrKeepProfileChangesClosed.emit()
- self.getMachineManager().activeQualityChanged.emit()
+ for extruder in global_stack.extruders.values():
+ extruder.userChanges.update()
+ global_stack.userChanges.update()
@pyqtSlot(int)
def messageBoxClosed(self, button):
@@ -504,7 +477,7 @@ class CuraApplication(QtApplication):
#
# Note that the AutoSave plugin also calls this method.
def saveSettings(self):
- if not self._started: # Do not do saving during application start
+ if not self.started: # Do not do saving during application start
return
ContainerRegistry.getInstance().saveDirtyContainers()
@@ -525,12 +498,23 @@ class CuraApplication(QtApplication):
def getStaticVersion(cls):
return CuraVersion
+ ## Handle removing the unneeded plugins
+ # \sa PluginRegistry
+ def _removePlugins(self):
+ self._plugin_registry.removePlugins()
+
## Handle loading of all plugin types (and the backend explicitly)
- # \sa PluginRegistery
+ # \sa PluginRegistry
def _loadPlugins(self):
self._plugin_registry.addType("profile_reader", self._addProfileReader)
self._plugin_registry.addType("profile_writer", self._addProfileWriter)
- self._plugin_registry.addPluginLocation(os.path.join(QtApplication.getInstallPrefix(), "lib", "cura"))
+
+ if Platform.isLinux():
+ lib_suffixes = {"", "64", "32", "x32"} #A few common ones on different distributions.
+ else:
+ lib_suffixes = {""}
+ for suffix in lib_suffixes:
+ self._plugin_registry.addPluginLocation(os.path.join(QtApplication.getInstallPrefix(), "lib" + suffix, "cura"))
if not hasattr(sys, "frozen"):
self._plugin_registry.addPluginLocation(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "plugins"))
self._plugin_registry.loadPlugin("ConsoleLogger")
@@ -658,6 +642,29 @@ class CuraApplication(QtApplication):
def run(self):
self.preRun()
+ container_registry = ContainerRegistry.getInstance()
+
+ Logger.log("i", "Initializing variant manager")
+ self._variant_manager = VariantManager(container_registry)
+ self._variant_manager.initialize()
+
+ Logger.log("i", "Initializing material manager")
+ from cura.Machines.MaterialManager import MaterialManager
+ self._material_manager = MaterialManager(container_registry, parent = self)
+ self._material_manager.initialize()
+
+ Logger.log("i", "Initializing quality manager")
+ from cura.Machines.QualityManager import QualityManager
+ self._quality_manager = QualityManager(container_registry, parent = self)
+ self._quality_manager.initialize()
+
+ Logger.log("i", "Initializing machine manager")
+ self._machine_manager = MachineManager(self)
+
+ Logger.log("i", "Initializing machine error checker")
+ self._machine_error_checker = MachineErrorChecker(self)
+ self._machine_error_checker.initialize()
+
# Check if we should run as single instance or not
self._setUpSingleInstanceServer()
@@ -670,6 +677,11 @@ class CuraApplication(QtApplication):
self._print_information = PrintInformation.PrintInformation()
self._cura_actions = CuraActions.CuraActions(self)
+ # Initialize setting visibility presets model
+ self._setting_visibility_presets_model = SettingVisibilityPresetsModel(self)
+ default_visibility_profile = self._setting_visibility_presets_model.getItem(0)
+ Preferences.getInstance().setDefault("general/visible_settings", ";".join(default_visibility_profile["settings"]))
+
# Detect in which mode to run and execute that mode
if self.getCommandLineOption("headless", False):
self.runWithoutGUI()
@@ -682,9 +694,12 @@ class CuraApplication(QtApplication):
for file_name in self._open_file_queue: # Open all the files that were queued up while plug-ins were loading.
self._openFile(file_name)
- self._started = True
+ self.started = True
+ self.initializationFinished.emit()
self.exec_()
+ initializationFinished = pyqtSignal()
+
## Run Cura without GUI elements and interaction (server mode).
def runWithoutGUI(self):
self._use_gui = False
@@ -749,9 +764,16 @@ class CuraApplication(QtApplication):
def hasGui(self):
return self._use_gui
+ @pyqtSlot(result = QObject)
+ def getSettingVisibilityPresetsModel(self, *args) -> SettingVisibilityPresetsModel:
+ return self._setting_visibility_presets_model
+
+ def getMachineErrorChecker(self, *args) -> MachineErrorChecker:
+ return self._machine_error_checker
+
def getMachineManager(self, *args) -> MachineManager:
if self._machine_manager is None:
- self._machine_manager = MachineManager.createMachineManager()
+ self._machine_manager = MachineManager(self)
return self._machine_manager
def getExtruderManager(self, *args):
@@ -759,20 +781,32 @@ class CuraApplication(QtApplication):
self._extruder_manager = ExtruderManager.createExtruderManager()
return self._extruder_manager
+ def getVariantManager(self, *args):
+ return self._variant_manager
+
+ @pyqtSlot(result = QObject)
def getMaterialManager(self, *args):
- if self._material_manager is None:
- self._material_manager = MaterialManager.createMaterialManager()
return self._material_manager
+ @pyqtSlot(result = QObject)
+ def getQualityManager(self, *args):
+ return self._quality_manager
+
def getObjectsModel(self, *args):
if self._object_manager is None:
self._object_manager = ObjectsModel.createObjectsModel()
return self._object_manager
+ @pyqtSlot(result = QObject)
+ def getMultiBuildPlateModel(self, *args):
+ if self._multi_build_plate_model is None:
+ self._multi_build_plate_model = MultiBuildPlateModel(self)
+ return self._multi_build_plate_model
+
+ @pyqtSlot(result = QObject)
def getBuildPlateModel(self, *args):
if self._build_plate_model is None:
- self._build_plate_model = BuildPlateModel.createBuildPlateModel()
-
+ self._build_plate_model = BuildPlateModel(self)
return self._build_plate_model
def getCuraSceneController(self, *args):
@@ -810,6 +844,16 @@ class CuraApplication(QtApplication):
def getPrintInformation(self):
return self._print_information
+ def getQualityProfilesDropDownMenuModel(self, *args, **kwargs):
+ if self._quality_profile_drop_down_menu_model is None:
+ self._quality_profile_drop_down_menu_model = QualityProfilesDropDownMenuModel(self)
+ return self._quality_profile_drop_down_menu_model
+
+ def getCustomQualityProfilesDropDownMenuModel(self, *args, **kwargs):
+ if self._custom_quality_profile_drop_down_menu_model is None:
+ self._custom_quality_profile_drop_down_menu_model = CustomQualityProfilesDropDownMenuModel(self)
+ return self._custom_quality_profile_drop_down_menu_model
+
## Registers objects for the QML engine to use.
#
# \param engine The QML engine.
@@ -824,27 +868,36 @@ class CuraApplication(QtApplication):
qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type")
- qmlRegisterSingletonType(CuraSceneController, "Cura", 1, 2, "SceneController", self.getCuraSceneController)
+ qmlRegisterSingletonType(CuraSceneController, "Cura", 1, 0, "SceneController", self.getCuraSceneController)
qmlRegisterSingletonType(ExtruderManager, "Cura", 1, 0, "ExtruderManager", self.getExtruderManager)
qmlRegisterSingletonType(MachineManager, "Cura", 1, 0, "MachineManager", self.getMachineManager)
- qmlRegisterSingletonType(MaterialManager, "Cura", 1, 0, "MaterialManager", self.getMaterialManager)
qmlRegisterSingletonType(SettingInheritanceManager, "Cura", 1, 0, "SettingInheritanceManager", self.getSettingInheritanceManager)
- qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 2, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager)
+ qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager)
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
- qmlRegisterSingletonType(ObjectsModel, "Cura", 1, 2, "ObjectsModel", self.getObjectsModel)
- qmlRegisterSingletonType(BuildPlateModel, "Cura", 1, 2, "BuildPlateModel", self.getBuildPlateModel)
+ qmlRegisterSingletonType(ObjectsModel, "Cura", 1, 0, "ObjectsModel", self.getObjectsModel)
+ qmlRegisterType(BuildPlateModel, "Cura", 1, 0, "BuildPlateModel")
+ qmlRegisterType(MultiBuildPlateModel, "Cura", 1, 0, "MultiBuildPlateModel")
qmlRegisterType(InstanceContainer, "Cura", 1, 0, "InstanceContainer")
qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel")
- qmlRegisterType(ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel")
- qmlRegisterSingletonType(ProfilesModel, "Cura", 1, 0, "ProfilesModel", ProfilesModel.createProfilesModel)
- qmlRegisterType(MaterialsModel, "Cura", 1, 0, "MaterialsModel")
- qmlRegisterType(QualityAndUserProfilesModel, "Cura", 1, 0, "QualityAndUserProfilesModel")
- qmlRegisterType(UserProfilesModel, "Cura", 1, 0, "UserProfilesModel")
+
+ qmlRegisterType(GenericMaterialsModel, "Cura", 1, 0, "GenericMaterialsModel")
+ qmlRegisterType(BrandMaterialsModel, "Cura", 1, 0, "BrandMaterialsModel")
+ qmlRegisterType(MaterialManagementModel, "Cura", 1, 0, "MaterialManagementModel")
+ qmlRegisterType(QualityManagementModel, "Cura", 1, 0, "QualityManagementModel")
+ qmlRegisterType(MachineManagementModel, "Cura", 1, 0, "MachineManagementModel")
+
+ qmlRegisterSingletonType(QualityProfilesDropDownMenuModel, "Cura", 1, 0,
+ "QualityProfilesDropDownMenuModel", self.getQualityProfilesDropDownMenuModel)
+ qmlRegisterSingletonType(CustomQualityProfilesDropDownMenuModel, "Cura", 1, 0,
+ "CustomQualityProfilesDropDownMenuModel", self.getCustomQualityProfilesDropDownMenuModel)
+ qmlRegisterType(NozzleModel, "Cura", 1, 0, "NozzleModel")
+
qmlRegisterType(MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler")
+ qmlRegisterType(SettingVisibilityPresetsModel, "Cura", 1, 0, "SettingVisibilityPresetsModel")
qmlRegisterType(QualitySettingsModel, "Cura", 1, 0, "QualitySettingsModel")
qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator")
- qmlRegisterType(UserChangesModel, "Cura", 1, 1, "UserChangesModel")
+ qmlRegisterType(UserChangesModel, "Cura", 1, 0, "UserChangesModel")
qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager)
# As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work.
@@ -921,12 +974,16 @@ class CuraApplication(QtApplication):
def getSceneBoundingBoxString(self):
return self._i18n_catalog.i18nc("@info 'width', 'depth' and 'height' are variable names that must NOT be translated; just translate the format of ##x##x## mm.", "%(width).1f x %(depth).1f x %(height).1f mm") % {'width' : self._scene_bounding_box.width.item(), 'depth': self._scene_bounding_box.depth.item(), 'height' : self._scene_bounding_box.height.item()}
+ def updatePlatformActivityDelayed(self, node = None):
+ if node is not None and node.getMeshData() is not None:
+ self._update_platform_activity_timer.start()
+
## Update scene bounding box for current build plate
def updatePlatformActivity(self, node = None):
count = 0
scene_bounding_box = None
is_block_slicing_node = False
- active_build_plate = self.getBuildPlateModel().activeBuildPlate
+ active_build_plate = self.getMultiBuildPlateModel().activeBuildPlate
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if (
not issubclass(type(node), CuraSceneNode) or
@@ -1053,7 +1110,7 @@ class CuraApplication(QtApplication):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
- if node.getParent() and node.getParent().callDecoration("isGroup"):
+ if node.getParent() and node.getParent().callDecoration("isGroup") or node.getParent().callDecoration("isSliceable"):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
if not node.isSelectable():
continue # i.e. node with layer data
@@ -1175,7 +1232,7 @@ class CuraApplication(QtApplication):
@pyqtSlot()
def arrangeAll(self):
nodes = []
- active_build_plate = self.getBuildPlateModel().activeBuildPlate
+ active_build_plate = self.getMultiBuildPlateModel().activeBuildPlate
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if not isinstance(node, SceneNode):
continue
@@ -1228,8 +1285,11 @@ class CuraApplication(QtApplication):
def reloadAll(self):
Logger.log("i", "Reloading all loaded mesh data.")
nodes = []
+ has_merged_nodes = False
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
- if not isinstance(node, SceneNode) or not node.getMeshData():
+ if not isinstance(node, CuraSceneNode) or not node.getMeshData() :
+ if node.getName() == "MergedMesh":
+ has_merged_nodes = True
continue
nodes.append(node)
@@ -1243,10 +1303,14 @@ class CuraApplication(QtApplication):
job = ReadMeshJob(file_name)
job._node = node
job.finished.connect(self._reloadMeshFinished)
+ if has_merged_nodes:
+ job.finished.connect(self.updateOriginOfMergedMeshes)
+
job.start()
else:
Logger.log("w", "Unable to reload data because we don't have a filename.")
+
## Get logging data of the backend engine
# \returns \type{string} Logging data
@pyqtSlot(result = str)
@@ -1316,6 +1380,58 @@ class CuraApplication(QtApplication):
# Use the previously found center of the group bounding box as the new location of the group
group_node.setPosition(group_node.getBoundingBox().center)
+ group_node.setName("MergedMesh") # add a specific name to distinguish this node
+
+
+ ## Updates origin position of all merged meshes
+ # \param jobNode \type{Job} empty object which passed which is required by JobQueue
+ def updateOriginOfMergedMeshes(self, jobNode):
+ group_nodes = []
+ for node in DepthFirstIterator(self.getController().getScene().getRoot()):
+ if isinstance(node, CuraSceneNode) and node.getName() == "MergedMesh":
+
+ #checking by name might be not enough, the merged mesh should has "GroupDecorator" decorator
+ for decorator in node.getDecorators():
+ if isinstance(decorator, GroupDecorator):
+ group_nodes.append(node)
+ break
+
+ for group_node in group_nodes:
+ meshes = [node.getMeshData() for node in group_node.getAllChildren() if node.getMeshData()]
+
+ # Compute the center of the objects
+ object_centers = []
+ # Forget about the translation that the original objects have
+ zero_translation = Matrix(data=numpy.zeros(3))
+ for mesh, node in zip(meshes, group_node.getChildren()):
+ transformation = node.getLocalTransformation()
+ transformation.setTranslation(zero_translation)
+ transformed_mesh = mesh.getTransformed(transformation)
+ center = transformed_mesh.getCenterPosition()
+ if center is not None:
+ object_centers.append(center)
+
+ if object_centers and len(object_centers) > 0:
+ middle_x = sum([v.x for v in object_centers]) / len(object_centers)
+ middle_y = sum([v.y for v in object_centers]) / len(object_centers)
+ middle_z = sum([v.z for v in object_centers]) / len(object_centers)
+ offset = Vector(middle_x, middle_y, middle_z)
+ else:
+ offset = Vector(0, 0, 0)
+
+ # Move each node to the same position.
+ for mesh, node in zip(meshes, group_node.getChildren()):
+ transformation = node.getLocalTransformation()
+ transformation.setTranslation(zero_translation)
+ transformed_mesh = mesh.getTransformed(transformation)
+
+ # Align the object around its zero position
+ # and also apply the offset to center it inside the group.
+ node.setPosition(-transformed_mesh.getZeroPosition() - offset)
+
+ # Use the previously found center of the group bounding box as the new location of the group
+ group_node.setPosition(group_node.getBoundingBox().center)
+
@pyqtSlot()
def groupSelected(self):
@@ -1324,13 +1440,19 @@ class CuraApplication(QtApplication):
group_decorator = GroupDecorator()
group_node.addDecorator(group_decorator)
group_node.addDecorator(ConvexHullDecorator())
- group_node.addDecorator(BuildPlateDecorator(self.getBuildPlateModel().activeBuildPlate))
+ group_node.addDecorator(BuildPlateDecorator(self.getMultiBuildPlateModel().activeBuildPlate))
group_node.setParent(self.getController().getScene().getRoot())
group_node.setSelectable(True)
center = Selection.getSelectionCenter()
group_node.setPosition(center)
group_node.setCenterPosition(center)
+ # Remove nodes that are directly parented to another selected node from the selection so they remain parented
+ selected_nodes = Selection.getAllSelectedObjects().copy()
+ for node in selected_nodes:
+ if node.getParent() in selected_nodes and not node.getParent().callDecoration("isGroup"):
+ Selection.remove(node)
+
# Move selected nodes into the group-node
Selection.applyOperation(SetParentOperation, group_node)
@@ -1349,6 +1471,10 @@ class CuraApplication(QtApplication):
group_parent = node.getParent()
children = node.getChildren().copy()
for child in children:
+ # Ungroup only 1 level deep
+ if child.getParent() != node:
+ continue
+
# Set the parent of the children to the parent of the group-node
op.addOperation(SetParentOperation(child, group_parent))
@@ -1469,7 +1595,7 @@ class CuraApplication(QtApplication):
arrange_objects_on_load = (
not Preferences.getInstance().getValue("cura/use_multi_build_plate") or
not Preferences.getInstance().getValue("cura/not_arrange_objects_on_load"))
- target_build_plate = self.getBuildPlateModel().activeBuildPlate if arrange_objects_on_load else -1
+ target_build_plate = self.getMultiBuildPlateModel().activeBuildPlate if arrange_objects_on_load else -1
root = self.getController().getScene().getRoot()
fixed_nodes = []
@@ -1478,19 +1604,34 @@ class CuraApplication(QtApplication):
fixed_nodes.append(node_)
arranger = Arrange.create(fixed_nodes = fixed_nodes)
min_offset = 8
+ default_extruder_position = self.getMachineManager().defaultExtruderPosition
+ default_extruder_id = self._global_container_stack.extruders[default_extruder_position].getId()
for original_node in nodes:
# Create a CuraSceneNode just if the original node is not that type
- node = original_node if isinstance(original_node, CuraSceneNode) else CuraSceneNode()
- node.setMeshData(original_node.getMeshData())
+ if isinstance(original_node, CuraSceneNode):
+ node = original_node
+ else:
+ node = CuraSceneNode()
+ node.setMeshData(original_node.getMeshData())
+
+ #Setting meshdata does not apply scaling.
+ if(original_node.getScale() != Vector(1.0, 1.0, 1.0)):
+ node.scale(original_node.getScale())
+
node.setSelectable(True)
node.setName(os.path.basename(filename))
self.getBuildVolume().checkBoundsAndUpdate(node)
- extension = os.path.splitext(filename)[1]
- if extension.lower() in self._non_sliceable_extensions:
+ is_non_sliceable = False
+ filename_lower = filename.lower()
+ for extension in self._non_sliceable_extensions:
+ if filename_lower.endswith(extension):
+ is_non_sliceable = True
+ break
+ if is_non_sliceable:
self.callLater(lambda: self.getController().setActiveView("SimulationView"))
block_slicing_decorator = BlockSlicingDecorator()
@@ -1535,6 +1676,8 @@ class CuraApplication(QtApplication):
op = AddSceneNodeOperation(node, scene.getRoot())
op.push()
+
+ node.callDecoration("setActiveExtruder", default_extruder_id)
scene.sceneChanged.emit(node)
self.fileCompleted.emit(filename)
@@ -1555,7 +1698,7 @@ class CuraApplication(QtApplication):
result = workspace_reader.preRead(file_path, show_dialog=False)
return result == WorkspaceReader.PreReadResult.accepted
except Exception as e:
- Logger.log("e", "Could not check file %s: %s", file_url, e)
+ Logger.logException("e", "Could not check file %s: %s", file_url)
return False
def _onContextMenuRequested(self, x: float, y: float) -> None:
diff --git a/cura/Machines/ContainerNode.py b/cura/Machines/ContainerNode.py
new file mode 100644
index 0000000000..6a839fb921
--- /dev/null
+++ b/cura/Machines/ContainerNode.py
@@ -0,0 +1,49 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from typing import Optional
+
+from collections import OrderedDict
+
+from UM.Logger import Logger
+from UM.Settings.InstanceContainer import InstanceContainer
+
+
+##
+# A metadata / container combination. Use getContainer() to get the container corresponding to the metadata.
+#
+# ContainerNode is a multi-purpose class. It has two main purposes:
+# 1. It encapsulates an InstanceContainer. It contains that InstanceContainer's
+# - metadata (Always)
+# - container (lazy-loaded when needed)
+# 2. It also serves as a node in a hierarchical InstanceContainer lookup table/tree.
+# This is used in Variant, Material, and Quality Managers.
+#
+class ContainerNode:
+ __slots__ = ("metadata", "container", "children_map")
+
+ def __init__(self, metadata: Optional[dict] = None):
+ self.metadata = metadata
+ self.container = None
+ self.children_map = OrderedDict()
+
+ def getChildNode(self, child_key: str) -> Optional["ContainerNode"]:
+ return self.children_map.get(child_key)
+
+ def getContainer(self) -> "InstanceContainer":
+ if self.metadata is None:
+ raise RuntimeError("Cannot get container for a ContainerNode without metadata")
+
+ if self.container is None:
+ container_id = self.metadata["id"]
+ Logger.log("i", "Lazy-loading container [%s]", container_id)
+ from UM.Settings.ContainerRegistry import ContainerRegistry
+ container_list = ContainerRegistry.getInstance().findInstanceContainers(id = container_id)
+ if not container_list:
+ raise RuntimeError("Failed to lazy-load container [%s], cannot find it" % container_id)
+ self.container = container_list[0]
+
+ return self.container
+
+ def __str__(self) -> str:
+ return "%s[%s]" % (self.__class__.__name__, self.metadata.get("id"))
diff --git a/cura/Machines/MachineErrorChecker.py b/cura/Machines/MachineErrorChecker.py
new file mode 100644
index 0000000000..37de4f30ce
--- /dev/null
+++ b/cura/Machines/MachineErrorChecker.py
@@ -0,0 +1,181 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+import time
+
+from collections import deque
+
+from PyQt5.QtCore import QObject, QTimer, pyqtSignal, pyqtProperty
+
+from UM.Application import Application
+from UM.Logger import Logger
+from UM.Settings.SettingDefinition import SettingDefinition
+from UM.Settings.Validator import ValidatorState
+
+
+#
+# This class performs setting error checks for the currently active machine.
+#
+# The whole error checking process is pretty heavy which can take ~0.5 secs, so it can cause GUI to lag.
+# The idea here is to split the whole error check into small tasks, each of which only checks a single setting key
+# in a stack. According to my profiling results, the maximal runtime for such a sub-task is <0.03 secs, which should
+# be good enough. Moreover, if any changes happened to the machine, we can cancel the check in progress without wait
+# for it to finish the complete work.
+#
+class MachineErrorChecker(QObject):
+
+ def __init__(self, parent = None):
+ super().__init__(parent)
+
+ self._global_stack = None
+
+ self._has_errors = True # Result of the error check, indicating whether there are errors in the stack
+ self._error_keys = set() # A set of settings keys that have errors
+ self._error_keys_in_progress = set() # The variable that stores the results of the currently in progress check
+
+ self._stacks_and_keys_to_check = None # a FIFO queue of tuples (stack, key) to check for errors
+
+ self._need_to_check = False # Whether we need to schedule a new check or not. This flag is set when a new
+ # error check needs to take place while there is already one running at the moment.
+ self._check_in_progress = False # Whether there is an error check running in progress at the moment.
+
+ self._application = Application.getInstance()
+ self._machine_manager = self._application.getMachineManager()
+
+ self._start_time = 0 # measure checking time
+
+ # This timer delays the starting of error check so we can react less frequently if the user is frequently
+ # changing settings.
+ self._error_check_timer = QTimer(self)
+ self._error_check_timer.setInterval(100)
+ self._error_check_timer.setSingleShot(True)
+
+ def initialize(self):
+ self._error_check_timer.timeout.connect(self._rescheduleCheck)
+
+ # Reconnect all signals when the active machine gets changed.
+ self._machine_manager.globalContainerChanged.connect(self._onMachineChanged)
+
+ # Whenever the machine settings get changed, we schedule an error check.
+ self._machine_manager.globalContainerChanged.connect(self.startErrorCheck)
+ self._machine_manager.globalValueChanged.connect(self.startErrorCheck)
+
+ self._onMachineChanged()
+
+ def _onMachineChanged(self):
+ if self._global_stack:
+ self._global_stack.propertyChanged.disconnect(self.startErrorCheck)
+ self._global_stack.containersChanged.disconnect(self.startErrorCheck)
+
+ for extruder in self._global_stack.extruders.values():
+ extruder.propertyChanged.disconnect(self.startErrorCheck)
+ extruder.containersChanged.disconnect(self.startErrorCheck)
+
+ self._global_stack = self._machine_manager.activeMachine
+
+ if self._global_stack:
+ self._global_stack.propertyChanged.connect(self.startErrorCheck)
+ self._global_stack.containersChanged.connect(self.startErrorCheck)
+
+ for extruder in self._global_stack.extruders.values():
+ extruder.propertyChanged.connect(self.startErrorCheck)
+ extruder.containersChanged.connect(self.startErrorCheck)
+
+ hasErrorUpdated = pyqtSignal()
+ needToWaitForResultChanged = pyqtSignal()
+ errorCheckFinished = pyqtSignal()
+
+ @pyqtProperty(bool, notify = hasErrorUpdated)
+ def hasError(self) -> bool:
+ return self._has_errors
+
+ @pyqtProperty(bool, notify = needToWaitForResultChanged)
+ def needToWaitForResult(self) -> bool:
+ return self._need_to_check or self._check_in_progress
+
+ # Starts the error check timer to schedule a new error check.
+ def startErrorCheck(self, *args):
+ if not self._check_in_progress:
+ self._need_to_check = True
+ self.needToWaitForResultChanged.emit()
+ self._error_check_timer.start()
+
+ # This function is called by the timer to reschedule a new error check.
+ # If there is no check in progress, it will start a new one. If there is any, it sets the "_need_to_check" flag
+ # to notify the current check to stop and start a new one.
+ def _rescheduleCheck(self):
+ if self._check_in_progress and not self._need_to_check:
+ self._need_to_check = True
+ self.needToWaitForResultChanged.emit()
+ return
+
+ self._error_keys_in_progress = set()
+ self._need_to_check = False
+ self.needToWaitForResultChanged.emit()
+
+ global_stack = self._machine_manager.activeMachine
+ if global_stack is None:
+ Logger.log("i", "No active machine, nothing to check.")
+ return
+
+ # Populate the (stack, key) tuples to check
+ self._stacks_and_keys_to_check = deque()
+ for stack in [global_stack] + list(global_stack.extruders.values()):
+ for key in stack.getAllKeys():
+ self._stacks_and_keys_to_check.append((stack, key))
+
+ self._application.callLater(self._checkStack)
+ self._start_time = time.time()
+ Logger.log("d", "New error check scheduled.")
+
+ def _checkStack(self):
+ if self._need_to_check:
+ Logger.log("d", "Need to check for errors again. Discard the current progress and reschedule a check.")
+ self._check_in_progress = False
+ self._application.callLater(self.startErrorCheck)
+ return
+
+ self._check_in_progress = True
+
+ # If there is nothing to check any more, it means there is no error.
+ if not self._stacks_and_keys_to_check:
+ # Finish
+ self._setResult(False)
+ return
+
+ # Get the next stack and key to check
+ stack, key = self._stacks_and_keys_to_check.popleft()
+
+ enabled = stack.getProperty(key, "enabled")
+ if not enabled:
+ self._application.callLater(self._checkStack)
+ return
+
+ validation_state = stack.getProperty(key, "validationState")
+ if validation_state is None:
+ # Setting is not validated. This can happen if there is only a setting definition.
+ # We do need to validate it, because a setting definitions value can be set by a function, which could
+ # be an invalid setting.
+ definition = stack.getSettingDefinition(key)
+ validator_type = SettingDefinition.getValidatorForType(definition.type)
+ if validator_type:
+ validator = validator_type(key)
+ validation_state = validator(stack)
+ if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
+ # Finish
+ self._setResult(True)
+ return
+
+ # Schedule the check for the next key
+ self._application.callLater(self._checkStack)
+
+ def _setResult(self, result: bool):
+ if result != self._has_errors:
+ self._has_errors = result
+ self.hasErrorUpdated.emit()
+ self._machine_manager.stacksValidationChanged.emit()
+ self._need_to_check = False
+ self._check_in_progress = False
+ self.needToWaitForResultChanged.emit()
+ self.errorCheckFinished.emit()
+ Logger.log("i", "Error check finished, result = %s, time = %0.1fs", result, time.time() - self._start_time)
diff --git a/cura/Machines/MaterialGroup.py b/cura/Machines/MaterialGroup.py
new file mode 100644
index 0000000000..93c8a227a8
--- /dev/null
+++ b/cura/Machines/MaterialGroup.py
@@ -0,0 +1,28 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from typing import List
+from cura.Machines.MaterialNode import MaterialNode #For type checking.
+
+## A MaterialGroup represents a group of material InstanceContainers that are derived from a single material profile.
+# The main InstanceContainer which has the ID of the material profile file name is called the "root_material". For
+# example: "generic_abs" is the root material (ID) of "generic_abs_ultimaker3" and "generic_abs_ultimaker3_AA_0.4",
+# and "generic_abs_ultimaker3" and "generic_abs_ultimaker3_AA_0.4" are derived materials of "generic_abs".
+#
+# Using "generic_abs" as an example, the MaterialGroup for "generic_abs" will contain the following information:
+# - name: "generic_abs", root_material_id
+# - root_material_node: MaterialNode of "generic_abs"
+# - derived_material_node_list: A list of MaterialNodes that are derived from "generic_abs",
+# so "generic_abs_ultimaker3", "generic_abs_ultimaker3_AA_0.4", etc.
+#
+class MaterialGroup:
+ __slots__ = ("name", "is_read_only", "root_material_node", "derived_material_node_list")
+
+ def __init__(self, name: str, root_material_node: MaterialNode):
+ self.name = name
+ self.is_read_only = False
+ self.root_material_node = root_material_node
+ self.derived_material_node_list = [] #type: List[MaterialNode]
+
+ def __str__(self) -> str:
+ return "%s[%s]" % (self.__class__.__name__, self.name)
diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py
new file mode 100644
index 0000000000..24c7ccb8c0
--- /dev/null
+++ b/cura/Machines/MaterialManager.py
@@ -0,0 +1,519 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from collections import defaultdict, OrderedDict
+import copy
+import uuid
+from typing import Optional, TYPE_CHECKING
+
+from PyQt5.Qt import QTimer, QObject, pyqtSignal, pyqtSlot
+
+from UM.Application import Application
+from UM.Logger import Logger
+from UM.Settings.ContainerRegistry import ContainerRegistry
+from UM.Settings.SettingFunction import SettingFunction
+from UM.Util import parseBool
+
+from .MaterialNode import MaterialNode
+from .MaterialGroup import MaterialGroup
+
+if TYPE_CHECKING:
+ from cura.Settings.GlobalStack import GlobalStack
+
+
+#
+# MaterialManager maintains a number of maps and trees for material lookup.
+# The models GUI and QML use are now only dependent on the MaterialManager. That means as long as the data in
+# MaterialManager gets updated correctly, the GUI models should be updated correctly too, and the same goes for GUI.
+#
+# For now, updating the lookup maps and trees here is very simple: we discard the old data completely and recreate them
+# again. This means the update is exactly the same as initialization. There are performance concerns about this approach
+# but so far the creation of the tables and maps is very fast and there is no noticeable slowness, we keep it like this
+# because it's simple.
+#
+class MaterialManager(QObject):
+
+ materialsUpdated = pyqtSignal() # Emitted whenever the material lookup tables are updated.
+
+ def __init__(self, container_registry, parent = None):
+ super().__init__(parent)
+ self._application = Application.getInstance()
+ self._container_registry = container_registry # type: ContainerRegistry
+
+ self._fallback_materials_map = dict() # material_type -> generic material metadata
+ self._material_group_map = dict() # root_material_id -> MaterialGroup
+ self._diameter_machine_variant_material_map = dict() # approximate diameter str -> dict(machine_definition_id -> MaterialNode)
+
+ # We're using these two maps to convert between the specific diameter material id and the generic material id
+ # because the generic material ids are used in qualities and definitions, while the specific diameter material is meant
+ # i.e. generic_pla -> generic_pla_175
+ self._material_diameter_map = defaultdict(dict) # root_material_id -> approximate diameter str -> root_material_id for that diameter
+ self._diameter_material_map = dict() # material id including diameter (generic_pla_175) -> material root id (generic_pla)
+
+ # This is used in Legacy UM3 send material function and the material management page.
+ self._guid_material_groups_map = defaultdict(list) # GUID -> a list of material_groups
+
+ # The machine definition ID for the non-machine-specific materials.
+ # This is used as the last fallback option if the given machine-specific material(s) cannot be found.
+ self._default_machine_definition_id = "fdmprinter"
+ self._default_approximate_diameter_for_quality_search = "3"
+
+ # When a material gets added/imported, there can be more than one InstanceContainers. In those cases, we don't
+ # want to react on every container/metadata changed signal. The timer here is to buffer it a bit so we don't
+ # react too many time.
+ self._update_timer = QTimer(self)
+ self._update_timer.setInterval(300)
+ self._update_timer.setSingleShot(True)
+ self._update_timer.timeout.connect(self._updateMaps)
+
+ self._container_registry.containerMetaDataChanged.connect(self._onContainerMetadataChanged)
+ self._container_registry.containerAdded.connect(self._onContainerMetadataChanged)
+ self._container_registry.containerRemoved.connect(self._onContainerMetadataChanged)
+
+ def initialize(self):
+ # Find all materials and put them in a matrix for quick search.
+ material_metadatas = {metadata["id"]: metadata for metadata in self._container_registry.findContainersMetadata(type = "material")}
+
+ self._material_group_map = dict()
+
+ # Map #1
+ # root_material_id -> MaterialGroup
+ for material_id, material_metadata in material_metadatas.items():
+ # We don't store empty material in the lookup tables
+ if material_id == "empty_material":
+ continue
+
+ root_material_id = material_metadata.get("base_file")
+ if root_material_id not in self._material_group_map:
+ self._material_group_map[root_material_id] = MaterialGroup(root_material_id, MaterialNode(material_metadatas[root_material_id]))
+ self._material_group_map[root_material_id].is_read_only = self._container_registry.isReadOnly(root_material_id)
+ group = self._material_group_map[root_material_id]
+
+ #Store this material in the group of the appropriate root material.
+ if material_id != root_material_id:
+ new_node = MaterialNode(material_metadata)
+ group.derived_material_node_list.append(new_node)
+
+ # Order this map alphabetically so it's easier to navigate in a debugger
+ self._material_group_map = OrderedDict(sorted(self._material_group_map.items(), key = lambda x: x[0]))
+
+ # Map #1.5
+ # GUID -> material group list
+ self._guid_material_groups_map = defaultdict(list)
+ for root_material_id, material_group in self._material_group_map.items():
+ guid = material_group.root_material_node.metadata["GUID"]
+ self._guid_material_groups_map[guid].append(material_group)
+
+ # Map #2
+ # Lookup table for material type -> fallback material metadata, only for read-only materials
+ grouped_by_type_dict = dict()
+ material_types_without_fallback = set()
+ for root_material_id, material_node in self._material_group_map.items():
+ if not self._container_registry.isReadOnly(root_material_id):
+ continue
+ material_type = material_node.root_material_node.metadata["material"]
+ if material_type not in grouped_by_type_dict:
+ grouped_by_type_dict[material_type] = {"generic": None,
+ "others": []}
+ material_types_without_fallback.add(material_type)
+ brand = material_node.root_material_node.metadata["brand"]
+ if brand.lower() == "generic":
+ to_add = True
+ if material_type in grouped_by_type_dict:
+ diameter = material_node.root_material_node.metadata.get("approximate_diameter")
+ if diameter != self._default_approximate_diameter_for_quality_search:
+ to_add = False # don't add if it's not the default diameter
+ if to_add:
+ grouped_by_type_dict[material_type] = material_node.root_material_node.metadata
+ material_types_without_fallback.remove(material_type)
+ # Remove the materials that have no fallback materials
+ for material_type in material_types_without_fallback:
+ del grouped_by_type_dict[material_type]
+ self._fallback_materials_map = grouped_by_type_dict
+
+ # Map #3
+ # There can be multiple material profiles for the same material with different diameters, such as "generic_pla"
+ # and "generic_pla_175". This is inconvenient when we do material-specific quality lookup because a quality can
+ # be for either "generic_pla" or "generic_pla_175", but not both. This map helps to get the correct material ID
+ # for quality search.
+ self._material_diameter_map = defaultdict(dict)
+ self._diameter_material_map = dict()
+
+ # Group the material IDs by the same name, material, brand, and color but with different diameters.
+ material_group_dict = dict()
+ keys_to_fetch = ("name", "material", "brand", "color")
+ for root_material_id, machine_node in self._material_group_map.items():
+ if not self._container_registry.isReadOnly(root_material_id):
+ continue
+
+ root_material_metadata = machine_node.root_material_node.metadata
+
+ key_data = []
+ for key in keys_to_fetch:
+ key_data.append(root_material_metadata.get(key))
+ key_data = tuple(key_data)
+
+ if key_data not in material_group_dict:
+ material_group_dict[key_data] = dict()
+ approximate_diameter = root_material_metadata.get("approximate_diameter")
+ material_group_dict[key_data][approximate_diameter] = root_material_metadata["id"]
+
+ # Map [root_material_id][diameter] -> root_material_id for this diameter
+ for data_dict in material_group_dict.values():
+ for root_material_id1 in data_dict.values():
+ if root_material_id1 in self._material_diameter_map:
+ continue
+ diameter_map = data_dict
+ for root_material_id2 in data_dict.values():
+ self._material_diameter_map[root_material_id2] = diameter_map
+
+ default_root_material_id = data_dict.get(self._default_approximate_diameter_for_quality_search)
+ if default_root_material_id is None:
+ default_root_material_id = list(data_dict.values())[0] # no default diameter present, just take "the" only one
+ for root_material_id in data_dict.values():
+ self._diameter_material_map[root_material_id] = default_root_material_id
+
+ # Map #4
+ # "machine" -> "variant_name" -> "root material ID" -> specific material InstanceContainer
+ # Construct the "machine" -> "variant" -> "root material ID" -> specific material InstanceContainer
+ self._diameter_machine_variant_material_map = dict()
+ for material_metadata in material_metadatas.values():
+ # We don't store empty material in the lookup tables
+ if material_metadata["id"] == "empty_material":
+ continue
+
+ root_material_id = material_metadata["base_file"]
+ definition = material_metadata["definition"]
+ approximate_diameter = material_metadata["approximate_diameter"]
+
+ if approximate_diameter not in self._diameter_machine_variant_material_map:
+ self._diameter_machine_variant_material_map[approximate_diameter] = {}
+
+ machine_variant_material_map = self._diameter_machine_variant_material_map[approximate_diameter]
+ if definition not in machine_variant_material_map:
+ machine_variant_material_map[definition] = MaterialNode()
+
+ machine_node = machine_variant_material_map[definition]
+ variant_name = material_metadata.get("variant_name")
+ if not variant_name:
+ # if there is no variant, this material is for the machine, so put its metadata in the machine node.
+ machine_node.material_map[root_material_id] = MaterialNode(material_metadata)
+ else:
+ # this material is variant-specific, so we save it in a variant-specific node under the
+ # machine-specific node
+ if variant_name not in machine_node.children_map:
+ machine_node.children_map[variant_name] = MaterialNode()
+
+ variant_node = machine_node.children_map[variant_name]
+ if root_material_id not in variant_node.material_map:
+ variant_node.material_map[root_material_id] = MaterialNode(material_metadata)
+ else:
+ # Sanity check: make sure we don't have duplicated variant-specific materials for the same machine
+ raise RuntimeError("Found duplicate variant name [%s] for machine [%s] in material [%s]" %
+ (variant_name, definition, material_metadata["id"]))
+
+ self.materialsUpdated.emit()
+
+ def _updateMaps(self):
+ Logger.log("i", "Updating material lookup data ...")
+ self.initialize()
+
+ def _onContainerMetadataChanged(self, container):
+ self._onContainerChanged(container)
+
+ def _onContainerChanged(self, container):
+ container_type = container.getMetaDataEntry("type")
+ if container_type != "material":
+ return
+
+ # update the maps
+ self._update_timer.start()
+
+ def getMaterialGroup(self, root_material_id: str) -> Optional[MaterialGroup]:
+ return self._material_group_map.get(root_material_id)
+
+ def getRootMaterialIDForDiameter(self, root_material_id: str, approximate_diameter: str) -> str:
+ return self._material_diameter_map.get(root_material_id).get(approximate_diameter, root_material_id)
+
+ def getRootMaterialIDWithoutDiameter(self, root_material_id: str) -> str:
+ return self._diameter_material_map.get(root_material_id)
+
+ def getMaterialGroupListByGUID(self, guid: str) -> Optional[list]:
+ return self._guid_material_groups_map.get(guid)
+
+ #
+ # Return a dict with all root material IDs (k) and ContainerNodes (v) that's suitable for the given setup.
+ #
+ def getAvailableMaterials(self, machine_definition_id: str, extruder_variant_name: Optional[str],
+ diameter: float) -> dict:
+ # round the diameter to get the approximate diameter
+ rounded_diameter = str(round(diameter))
+ if rounded_diameter not in self._diameter_machine_variant_material_map:
+ Logger.log("i", "Cannot find materials with diameter [%s] (rounded to [%s])", diameter, rounded_diameter)
+ return dict()
+
+ # If there are variant materials, get the variant material
+ machine_variant_material_map = self._diameter_machine_variant_material_map[rounded_diameter]
+ machine_node = machine_variant_material_map.get(machine_definition_id)
+ default_machine_node = machine_variant_material_map.get(self._default_machine_definition_id)
+ variant_node = None
+ if extruder_variant_name is not None and machine_node is not None:
+ variant_node = machine_node.getChildNode(extruder_variant_name)
+
+ nodes_to_check = [variant_node, machine_node, default_machine_node]
+
+ # Fallback mechanism of finding materials:
+ # 1. variant-specific material
+ # 2. machine-specific material
+ # 3. generic material (for fdmprinter)
+ material_id_metadata_dict = dict()
+ for node in nodes_to_check:
+ if node is not None:
+ for material_id, node in node.material_map.items():
+ if material_id not in material_id_metadata_dict:
+ material_id_metadata_dict[material_id] = node
+
+ return material_id_metadata_dict
+
+ #
+ # A convenience function to get available materials for the given machine with the extruder position.
+ #
+ def getAvailableMaterialsForMachineExtruder(self, machine: "GlobalStack",
+ extruder_stack: "ExtruderStack") -> Optional[dict]:
+ machine_definition_id = machine.definition.getId()
+ variant_name = None
+ if extruder_stack.variant.getId() != "empty_variant":
+ variant_name = extruder_stack.variant.getName()
+ diameter = extruder_stack.approximateMaterialDiameter
+
+ # Fetch the available materials (ContainerNode) for the current active machine and extruder setup.
+ return self.getAvailableMaterials(machine_definition_id, variant_name, diameter)
+
+ #
+ # Gets MaterialNode for the given extruder and machine with the given material name.
+ # Returns None if:
+ # 1. the given machine doesn't have materials;
+ # 2. cannot find any material InstanceContainers with the given settings.
+ #
+ def getMaterialNode(self, machine_definition_id: str, extruder_variant_name: Optional[str],
+ diameter: float, root_material_id: str) -> Optional["InstanceContainer"]:
+ # round the diameter to get the approximate diameter
+ rounded_diameter = str(round(diameter))
+ if rounded_diameter not in self._diameter_machine_variant_material_map:
+ Logger.log("i", "Cannot find materials with diameter [%s] (rounded to [%s]) for root material id [%s]",
+ diameter, rounded_diameter, root_material_id)
+ return None
+
+ # If there are variant materials, get the variant material
+ machine_variant_material_map = self._diameter_machine_variant_material_map[rounded_diameter]
+ machine_node = machine_variant_material_map.get(machine_definition_id)
+ variant_node = None
+
+ # Fallback for "fdmprinter" if the machine-specific materials cannot be found
+ if machine_node is None:
+ machine_node = machine_variant_material_map.get(self._default_machine_definition_id)
+ if machine_node is not None and extruder_variant_name is not None:
+ variant_node = machine_node.getChildNode(extruder_variant_name)
+
+ # Fallback mechanism of finding materials:
+ # 1. variant-specific material
+ # 2. machine-specific material
+ # 3. generic material (for fdmprinter)
+ nodes_to_check = [variant_node, machine_node,
+ machine_variant_material_map.get(self._default_machine_definition_id)]
+
+ material_node = None
+ for node in nodes_to_check:
+ if node is not None:
+ material_node = node.material_map.get(root_material_id)
+ if material_node:
+ break
+
+ return material_node
+
+ #
+ # Gets MaterialNode for the given extruder and machine with the given material type.
+ # Returns None if:
+ # 1. the given machine doesn't have materials;
+ # 2. cannot find any material InstanceContainers with the given settings.
+ #
+ def getMaterialNodeByType(self, global_stack: "GlobalStack", extruder_variant_name: str, material_guid: str) -> Optional["MaterialNode"]:
+ node = None
+ machine_definition = global_stack.definition
+ if parseBool(machine_definition.getMetaDataEntry("has_materials", False)):
+ material_diameter = machine_definition.getProperty("material_diameter", "value")
+ if isinstance(material_diameter, SettingFunction):
+ material_diameter = material_diameter(global_stack)
+
+ # Look at the guid to material dictionary
+ root_material_id = None
+ for material_group in self._guid_material_groups_map[material_guid]:
+ if material_group.is_read_only:
+ root_material_id = material_group.root_material_node.metadata["id"]
+ break
+
+ if not root_material_id:
+ Logger.log("i", "Cannot find materials with guid [%s] ", material_guid)
+ return None
+
+ node = self.getMaterialNode(machine_definition.getId(), extruder_variant_name,
+ material_diameter, root_material_id)
+ return node
+
+ #
+ # Used by QualityManager. Built-in quality profiles may be based on generic material IDs such as "generic_pla".
+ # For materials such as ultimaker_pla_orange, no quality profiles may be found, so we should fall back to use
+ # the generic material IDs to search for qualities.
+ #
+ # An example would be, suppose we have machine with preferred material set to "filo3d_pla" (1.75mm), but its
+ # extruders only use 2.85mm materials, then we won't be able to find the preferred material for this machine.
+ # A fallback would be to fetch a generic material of the same type "PLA" as "filo3d_pla", and in this case it will
+ # be "generic_pla". This function is intended to get a generic fallback material for the given material type.
+ #
+ # This function returns the generic root material ID for the given material type, where material types are "PLA",
+ # "ABS", etc.
+ #
+ def getFallbackMaterialIdByMaterialType(self, material_type: str) -> Optional[str]:
+ # For safety
+ if material_type not in self._fallback_materials_map:
+ Logger.log("w", "The material type [%s] does not have a fallback material" % material_type)
+ return None
+ fallback_material = self._fallback_materials_map[material_type]
+ if fallback_material:
+ return self.getRootMaterialIDWithoutDiameter(fallback_material["id"])
+ else:
+ return None
+
+ def getDefaultMaterial(self, global_stack: "GlobalStack", extruder_variant_name: Optional[str]) -> Optional["MaterialNode"]:
+ node = None
+ machine_definition = global_stack.definition
+ if parseBool(global_stack.getMetaDataEntry("has_materials", False)):
+ material_diameter = machine_definition.getProperty("material_diameter", "value")
+ if isinstance(material_diameter, SettingFunction):
+ material_diameter = material_diameter(global_stack)
+ approximate_material_diameter = str(round(material_diameter))
+ root_material_id = machine_definition.getMetaDataEntry("preferred_material")
+ root_material_id = self.getRootMaterialIDForDiameter(root_material_id, approximate_material_diameter)
+ node = self.getMaterialNode(machine_definition.getId(), extruder_variant_name,
+ material_diameter, root_material_id)
+ return node
+
+ def removeMaterialByRootId(self, root_material_id: str):
+ material_group = self.getMaterialGroup(root_material_id)
+ if not material_group:
+ Logger.log("i", "Unable to remove the material with id %s, because it doesn't exist.", root_material_id)
+ return
+
+ 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"])
+
+ #
+ # Methods for GUI
+ #
+
+ #
+ # Sets the new name for the given material.
+ #
+ @pyqtSlot("QVariant", str)
+ def setMaterialName(self, material_node: "MaterialNode", name: str):
+ root_material_id = material_node.metadata["base_file"]
+ 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)
+ material_group.root_material_node.getContainer().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)
+
+ #
+ # Creates a duplicate of a material, which has the same GUID and base_file metadata.
+ # Returns the root material ID of the duplicated material if successful.
+ #
+ @pyqtSlot("QVariant", result = str)
+ def duplicateMaterial(self, material_node, new_base_id = None, new_metadata = None) -> Optional[str]:
+ root_material_id = material_node.metadata["base_file"]
+
+ material_group = self.getMaterialGroup(root_material_id)
+ if not material_group:
+ Logger.log("i", "Unable to duplicate the material with id %s, because it doesn't exist.", root_material_id)
+ return None
+
+ base_container = material_group.root_material_node.getContainer()
+
+ # Ensure all settings are saved.
+ self._application.saveSettings()
+
+ # Create a new ID & container to hold the data.
+ new_containers = []
+ if new_base_id is None:
+ new_base_id = self._container_registry.uniqueName(base_container.getId())
+ new_base_container = copy.deepcopy(base_container)
+ new_base_container.getMetaData()["id"] = new_base_id
+ new_base_container.getMetaData()["base_file"] = new_base_id
+ if new_metadata is not None:
+ for key, value in new_metadata.items():
+ new_base_container.getMetaData()[key] = value
+ new_containers.append(new_base_container)
+
+ # Clone all of them.
+ for node in material_group.derived_material_node_list:
+ container_to_copy = node.getContainer()
+ # Create unique IDs for every clone.
+ new_id = new_base_id
+ if container_to_copy.getMetaDataEntry("definition") != "fdmprinter":
+ new_id += "_" + container_to_copy.getMetaDataEntry("definition")
+ if container_to_copy.getMetaDataEntry("variant_name"):
+ variant_name = container_to_copy.getMetaDataEntry("variant_name")
+ new_id += "_" + variant_name.replace(" ", "_")
+
+ new_container = copy.deepcopy(container_to_copy)
+ new_container.getMetaData()["id"] = new_id
+ new_container.getMetaData()["base_file"] = new_base_id
+ if new_metadata is not None:
+ for key, value in new_metadata.items():
+ new_container.getMetaData()[key] = value
+
+ new_containers.append(new_container)
+
+ for container_to_add in new_containers:
+ container_to_add.setDirty(True)
+ self._container_registry.addContainer(container_to_add)
+ return new_base_id
+
+ #
+ # Create a new material by cloning Generic PLA for the current material diameter and generate a new GUID.
+ #
+ @pyqtSlot(result = str)
+ def createMaterial(self) -> str:
+ from UM.i18n import i18nCatalog
+ catalog = i18nCatalog("cura")
+ # Ensure all settings are saved.
+ self._application.saveSettings()
+
+ machine_manager = self._application.getMachineManager()
+ extruder_stack = machine_manager.activeStack
+
+ approximate_diameter = str(extruder_stack.approximateMaterialDiameter)
+ root_material_id = "generic_pla"
+ root_material_id = self.getRootMaterialIDForDiameter(root_material_id, approximate_diameter)
+ material_group = self.getMaterialGroup(root_material_id)
+
+ # 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"),
+ "brand": catalog.i18nc("@label", "Custom"),
+ "GUID": str(uuid.uuid4()),
+ }
+
+ self.duplicateMaterial(material_group.root_material_node,
+ new_base_id = new_id,
+ new_metadata = new_metadata)
+ return new_id
diff --git a/cura/Machines/MaterialNode.py b/cura/Machines/MaterialNode.py
new file mode 100644
index 0000000000..fde11186c2
--- /dev/null
+++ b/cura/Machines/MaterialNode.py
@@ -0,0 +1,21 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from typing import Optional
+
+from .ContainerNode import ContainerNode
+
+
+#
+# A MaterialNode is a node in the material lookup tree/map/table. It contains 2 (extra) fields:
+# - material_map: a one-to-one map of "material_root_id" to material_node.
+# - children_map: the key-value map for child nodes of this node. This is used in a lookup tree.
+#
+#
+class MaterialNode(ContainerNode):
+ __slots__ = ("material_map", "children_map")
+
+ def __init__(self, metadata: Optional[dict] = None):
+ super().__init__(metadata = metadata)
+ self.material_map = {} # material_root_id -> material_node
+ self.children_map = {} # mapping for the child nodes
diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py
new file mode 100644
index 0000000000..0a1337feeb
--- /dev/null
+++ b/cura/Machines/Models/BaseMaterialsModel.py
@@ -0,0 +1,68 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty
+
+from UM.Application import Application
+from UM.Qt.ListModel import ListModel
+
+
+#
+# This is the base model class for GenericMaterialsModel and BrandMaterialsModel
+# Those 2 models are used by the material drop down menu to show generic materials and branded materials separately.
+# The extruder position defined here is being used to bound a menu to the correct extruder. This is used in the top
+# bar menu "Settings" -> "Extruder nr" -> "Material" -> this menu
+#
+class BaseMaterialsModel(ListModel):
+ RootMaterialIdRole = Qt.UserRole + 1
+ IdRole = Qt.UserRole + 2
+ NameRole = Qt.UserRole + 3
+ BrandRole = Qt.UserRole + 4
+ MaterialRole = Qt.UserRole + 5
+ ColorRole = Qt.UserRole + 6
+ ContainerNodeRole = Qt.UserRole + 7
+
+ extruderPositionChanged = pyqtSignal()
+
+ def __init__(self, parent = None):
+ super().__init__(parent)
+ self._application = Application.getInstance()
+ self._machine_manager = self._application.getMachineManager()
+
+ self.addRoleName(self.RootMaterialIdRole, "root_material_id")
+ self.addRoleName(self.IdRole, "id")
+ self.addRoleName(self.NameRole, "name")
+ self.addRoleName(self.BrandRole, "brand")
+ self.addRoleName(self.MaterialRole, "material")
+ self.addRoleName(self.ColorRole, "color_name")
+ self.addRoleName(self.ContainerNodeRole, "container_node")
+
+ self._extruder_position = 0
+ self._extruder_stack = None
+
+ def _updateExtruderStack(self):
+ global_stack = self._machine_manager.activeMachine
+ if global_stack is None:
+ return
+
+ if self._extruder_stack is not None:
+ self._extruder_stack.pyqtContainersChanged.disconnect(self._update)
+ self._extruder_stack = global_stack.extruders.get(str(self._extruder_position))
+ if self._extruder_stack is not None:
+ self._extruder_stack.pyqtContainersChanged.connect(self._update)
+
+ def setExtruderPosition(self, position: int):
+ if self._extruder_position != position:
+ self._extruder_position = position
+ self._updateExtruderStack()
+ self.extruderPositionChanged.emit()
+
+ @pyqtProperty(int, fset = setExtruderPosition, notify = extruderPositionChanged)
+ def extruderPosition(self) -> int:
+ return self._extruder_position
+
+ #
+ # This is an abstract method that needs to be implemented by
+ #
+ def _update(self):
+ pass
diff --git a/cura/Machines/Models/BrandMaterialsModel.py b/cura/Machines/Models/BrandMaterialsModel.py
new file mode 100644
index 0000000000..f6c9a14632
--- /dev/null
+++ b/cura/Machines/Models/BrandMaterialsModel.py
@@ -0,0 +1,137 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty
+
+from UM.Qt.ListModel import ListModel
+from UM.Logger import Logger
+from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel
+
+
+#
+# This is an intermediate model to group materials with different colours for a same brand and type.
+#
+class MaterialsModelGroupedByType(ListModel):
+ NameRole = Qt.UserRole + 1
+ ColorsRole = Qt.UserRole + 2
+
+ def __init__(self, parent = None):
+ super().__init__(parent)
+
+ self.addRoleName(self.NameRole, "name")
+ self.addRoleName(self.ColorsRole, "colors")
+
+
+#
+# This model is used to show branded materials in the material drop down menu.
+# The structure of the menu looks like this:
+# Brand -> Material Type -> list of materials
+#
+# To illustrate, a branded material menu may look like this:
+# Ultimaker -> PLA -> Yellow PLA
+# -> Black PLA
+# -> ...
+# -> ABS -> White ABS
+# ...
+#
+class BrandMaterialsModel(ListModel):
+ NameRole = Qt.UserRole + 1
+ MaterialsRole = Qt.UserRole + 2
+
+ extruderPositionChanged = pyqtSignal()
+
+ def __init__(self, parent = None):
+ super().__init__(parent)
+
+ self.addRoleName(self.NameRole, "name")
+ self.addRoleName(self.MaterialsRole, "materials")
+
+ self._extruder_position = 0
+
+ from cura.CuraApplication import CuraApplication
+ self._machine_manager = CuraApplication.getInstance().getMachineManager()
+ self._extruder_manager = CuraApplication.getInstance().getExtruderManager()
+ self._material_manager = CuraApplication.getInstance().getMaterialManager()
+
+ self._machine_manager.activeStackChanged.connect(self._update) #Update when switching machines.
+ self._material_manager.materialsUpdated.connect(self._update) #Update when the list of materials changes.
+ self._update()
+
+ def setExtruderPosition(self, position: int):
+ if self._extruder_position != position:
+ self._extruder_position = position
+ self.extruderPositionChanged.emit()
+
+ @pyqtProperty(int, fset = setExtruderPosition, notify = extruderPositionChanged)
+ def extruderPosition(self) -> int:
+ return self._extruder_position
+
+ def _update(self):
+ Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
+ global_stack = self._machine_manager.activeMachine
+ if global_stack is None:
+ self.setItems([])
+ return
+ extruder_position = str(self._extruder_position)
+ if extruder_position not in global_stack.extruders:
+ self.setItems([])
+ return
+ extruder_stack = global_stack.extruders[str(self._extruder_position)]
+
+ available_material_dict = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack,
+ extruder_stack)
+ if available_material_dict is None:
+ self.setItems([])
+ return
+
+ brand_item_list = []
+ brand_group_dict = {}
+ for root_material_id, container_node in available_material_dict.items():
+ metadata = container_node.metadata
+ brand = metadata["brand"]
+ # Only add results for generic materials
+ if brand.lower() == "generic":
+ continue
+
+ if brand not in brand_group_dict:
+ brand_group_dict[brand] = {}
+
+ material_type = metadata["material"]
+ if material_type not in brand_group_dict[brand]:
+ brand_group_dict[brand][material_type] = []
+
+ item = {"root_material_id": root_material_id,
+ "id": metadata["id"],
+ "name": metadata["name"],
+ "brand": metadata["brand"],
+ "material": metadata["material"],
+ "color_name": metadata["color_name"],
+ "container_node": container_node
+ }
+ brand_group_dict[brand][material_type].append(item)
+
+ for brand, material_dict in brand_group_dict.items():
+ brand_item = {"name": brand,
+ "materials": MaterialsModelGroupedByType(self)}
+
+ material_type_item_list = []
+ for material_type, material_list in material_dict.items():
+ material_type_item = {"name": material_type,
+ "colors": BaseMaterialsModel(self)}
+ material_type_item["colors"].clear()
+
+ # Sort materials by name
+ material_list = sorted(material_list, key = lambda x: x["name"].upper())
+ material_type_item["colors"].setItems(material_list)
+
+ material_type_item_list.append(material_type_item)
+
+ # Sort material type by name
+ material_type_item_list = sorted(material_type_item_list, key = lambda x: x["name"].upper())
+ brand_item["materials"].setItems(material_type_item_list)
+
+ brand_item_list.append(brand_item)
+
+ # Sort brand by name
+ brand_item_list = sorted(brand_item_list, key = lambda x: x["name"].upper())
+ self.setItems(brand_item_list)
diff --git a/cura/Machines/Models/BuildPlateModel.py b/cura/Machines/Models/BuildPlateModel.py
new file mode 100644
index 0000000000..e1b4f40d8e
--- /dev/null
+++ b/cura/Machines/Models/BuildPlateModel.py
@@ -0,0 +1,51 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from PyQt5.QtCore import Qt
+
+from UM.Application import Application
+from UM.Logger import Logger
+from UM.Qt.ListModel import ListModel
+from UM.Util import parseBool
+
+from cura.Machines.VariantManager import VariantType
+
+
+class BuildPlateModel(ListModel):
+ NameRole = Qt.UserRole + 1
+ ContainerNodeRole = Qt.UserRole + 2
+
+ def __init__(self, parent = None):
+ super().__init__(parent)
+
+ self.addRoleName(self.NameRole, "name")
+ self.addRoleName(self.ContainerNodeRole, "container_node")
+
+ self._application = Application.getInstance()
+ self._variant_manager = self._application._variant_manager
+ self._machine_manager = self._application.getMachineManager()
+
+ self._machine_manager.globalContainerChanged.connect(self._update)
+
+ self._update()
+
+ def _update(self):
+ Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
+ global_stack = self._machine_manager._global_container_stack
+ if not global_stack:
+ self.setItems([])
+ return
+
+ has_variants = parseBool(global_stack.getMetaDataEntry("has_variant_buildplates", False))
+ if not has_variants:
+ self.setItems([])
+ return
+
+ variant_dict = self._variant_manager.getVariantNodes(global_stack, variant_type = VariantType.BUILD_PLATE)
+
+ item_list = []
+ for name, variant_node in variant_dict.items():
+ item = {"name": name,
+ "container_node": variant_node}
+ item_list.append(item)
+ self.setItems(item_list)
diff --git a/cura/Machines/Models/CustomQualityProfilesDropDownMenuModel.py b/cura/Machines/Models/CustomQualityProfilesDropDownMenuModel.py
new file mode 100644
index 0000000000..dcade8cb0d
--- /dev/null
+++ b/cura/Machines/Models/CustomQualityProfilesDropDownMenuModel.py
@@ -0,0 +1,37 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from UM.Logger import Logger
+
+from cura.Machines.Models.QualityProfilesDropDownMenuModel import QualityProfilesDropDownMenuModel
+
+
+#
+# This model is used for the custom profile items in the profile drop down menu.
+#
+class CustomQualityProfilesDropDownMenuModel(QualityProfilesDropDownMenuModel):
+
+ def _update(self):
+ Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
+
+ active_global_stack = self._machine_manager.activeMachine
+ if active_global_stack is None:
+ self.setItems([])
+ Logger.log("d", "No active GlobalStack, set %s as empty.", self.__class__.__name__)
+ return
+
+ quality_changes_group_dict = self._quality_manager.getQualityChangesGroups(active_global_stack)
+
+ item_list = []
+ for key in sorted(quality_changes_group_dict, key = lambda name: name.upper()):
+ quality_changes_group = quality_changes_group_dict[key]
+
+ item = {"name": quality_changes_group.name,
+ "layer_height": "",
+ "layer_height_without_unit": "",
+ "available": quality_changes_group.is_available,
+ "quality_changes_group": quality_changes_group}
+
+ item_list.append(item)
+
+ self.setItems(item_list)
diff --git a/cura/Machines/Models/GenericMaterialsModel.py b/cura/Machines/Models/GenericMaterialsModel.py
new file mode 100644
index 0000000000..2fac919f3e
--- /dev/null
+++ b/cura/Machines/Models/GenericMaterialsModel.py
@@ -0,0 +1,61 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from UM.Logger import Logger
+from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel
+
+
+class GenericMaterialsModel(BaseMaterialsModel):
+
+ def __init__(self, parent = None):
+ super().__init__(parent)
+
+ from cura.CuraApplication import CuraApplication
+ self._machine_manager = CuraApplication.getInstance().getMachineManager()
+ self._extruder_manager = CuraApplication.getInstance().getExtruderManager()
+ self._material_manager = CuraApplication.getInstance().getMaterialManager()
+
+ self._machine_manager.activeStackChanged.connect(self._update) #Update when switching machines.
+ self._material_manager.materialsUpdated.connect(self._update) #Update when the list of materials changes.
+ self._update()
+
+ def _update(self):
+ Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
+
+ global_stack = self._machine_manager.activeMachine
+ if global_stack is None:
+ self.setItems([])
+ return
+ extruder_position = str(self._extruder_position)
+ if extruder_position not in global_stack.extruders:
+ self.setItems([])
+ return
+ extruder_stack = global_stack.extruders[extruder_position]
+
+ available_material_dict = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack,
+ extruder_stack)
+ if available_material_dict is None:
+ self.setItems([])
+ return
+
+ item_list = []
+ for root_material_id, container_node in available_material_dict.items():
+ metadata = container_node.metadata
+ # Only add results for generic materials
+ if metadata["brand"].lower() != "generic":
+ continue
+
+ item = {"root_material_id": root_material_id,
+ "id": metadata["id"],
+ "name": metadata["name"],
+ "brand": metadata["brand"],
+ "material": metadata["material"],
+ "color_name": metadata["color_name"],
+ "container_node": container_node
+ }
+ item_list.append(item)
+
+ # Sort the item list by material name alphabetically
+ item_list = sorted(item_list, key = lambda d: d["name"].upper())
+
+ self.setItems(item_list)
diff --git a/cura/Machines/Models/MachineManagementModel.py b/cura/Machines/Models/MachineManagementModel.py
new file mode 100644
index 0000000000..7dc51f07f7
--- /dev/null
+++ b/cura/Machines/Models/MachineManagementModel.py
@@ -0,0 +1,82 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from UM.Qt.ListModel import ListModel
+
+from PyQt5.QtCore import Qt
+
+from UM.Settings.ContainerRegistry import ContainerRegistry
+from UM.Settings.ContainerStack import ContainerStack
+
+from UM.i18n import i18nCatalog
+catalog = i18nCatalog("cura")
+
+
+#
+# This the QML model for the quality management page.
+#
+class MachineManagementModel(ListModel):
+ NameRole = Qt.UserRole + 1
+ IdRole = Qt.UserRole + 2
+ MetaDataRole = Qt.UserRole + 3
+ GroupRole = Qt.UserRole + 4
+
+ def __init__(self, parent = None):
+ super().__init__(parent)
+ self.addRoleName(self.NameRole, "name")
+ self.addRoleName(self.IdRole, "id")
+ self.addRoleName(self.MetaDataRole, "metadata")
+ self.addRoleName(self.GroupRole, "group")
+ self._local_container_stacks = []
+ self._network_container_stacks = []
+
+ # Listen to changes
+ ContainerRegistry.getInstance().containerAdded.connect(self._onContainerChanged)
+ ContainerRegistry.getInstance().containerMetaDataChanged.connect(self._onContainerChanged)
+ ContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChanged)
+ self._filter_dict = {}
+ self._update()
+
+ ## Handler for container added/removed events from registry
+ def _onContainerChanged(self, container):
+ # We only need to update when the added / removed container is a stack.
+ if isinstance(container, ContainerStack) and container.getMetaDataEntry("type") == "machine":
+ self._update()
+
+ ## Private convenience function to reset & repopulate the model.
+ def _update(self):
+ items = []
+
+ # Get first the network enabled printers
+ network_filter_printers = {"type": "machine",
+ "um_network_key": "*",
+ "hidden": "False"}
+ self._network_container_stacks = ContainerRegistry.getInstance().findContainerStacks(**network_filter_printers)
+ self._network_container_stacks.sort(key = lambda i: i.getMetaDataEntry("connect_group_name"))
+
+ for container in self._network_container_stacks:
+ metadata = container.getMetaData().copy()
+ if container.getBottom():
+ metadata["definition_name"] = container.getBottom().getName()
+
+ items.append({"name": metadata["connect_group_name"],
+ "id": container.getId(),
+ "metadata": metadata,
+ "group": catalog.i18nc("@info:title", "Network enabled printers")})
+
+ # Get now the local printers
+ local_filter_printers = {"type": "machine", "um_network_key": None}
+ self._local_container_stacks = ContainerRegistry.getInstance().findContainerStacks(**local_filter_printers)
+ self._local_container_stacks.sort(key = lambda i: i.getName())
+
+ for container in self._local_container_stacks:
+ metadata = container.getMetaData().copy()
+ if container.getBottom():
+ metadata["definition_name"] = container.getBottom().getName()
+
+ items.append({"name": container.getName(),
+ "id": container.getId(),
+ "metadata": metadata,
+ "group": catalog.i18nc("@info:title", "Local printers")})
+
+ self.setItems(items)
diff --git a/cura/Machines/Models/MaterialManagementModel.py b/cura/Machines/Models/MaterialManagementModel.py
new file mode 100644
index 0000000000..46e9cb887a
--- /dev/null
+++ b/cura/Machines/Models/MaterialManagementModel.py
@@ -0,0 +1,104 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from PyQt5.QtCore import Qt
+
+from UM.Logger import Logger
+from UM.Qt.ListModel import ListModel
+
+
+#
+# This model is for the Material management page.
+#
+class MaterialManagementModel(ListModel):
+ RootMaterialIdRole = Qt.UserRole + 1
+ DisplayNameRole = Qt.UserRole + 2
+ BrandRole = Qt.UserRole + 3
+ MaterialTypeRole = Qt.UserRole + 4
+ ColorNameRole = Qt.UserRole + 5
+ ColorCodeRole = Qt.UserRole + 6
+ ContainerNodeRole = Qt.UserRole + 7
+ ContainerIdRole = Qt.UserRole + 8
+
+ DescriptionRole = Qt.UserRole + 9
+ AdhesionInfoRole = Qt.UserRole + 10
+ ApproximateDiameterRole = Qt.UserRole + 11
+ GuidRole = Qt.UserRole + 12
+ DensityRole = Qt.UserRole + 13
+ DiameterRole = Qt.UserRole + 14
+ IsReadOnlyRole = Qt.UserRole + 15
+
+ def __init__(self, parent = None):
+ super().__init__(parent)
+
+ self.addRoleName(self.RootMaterialIdRole, "root_material_id")
+ self.addRoleName(self.DisplayNameRole, "name")
+ self.addRoleName(self.BrandRole, "brand")
+ self.addRoleName(self.MaterialTypeRole, "material")
+ self.addRoleName(self.ColorNameRole, "color_name")
+ self.addRoleName(self.ColorCodeRole, "color_code")
+ self.addRoleName(self.ContainerNodeRole, "container_node")
+ self.addRoleName(self.ContainerIdRole, "container_id")
+
+ self.addRoleName(self.DescriptionRole, "description")
+ self.addRoleName(self.AdhesionInfoRole, "adhesion_info")
+ self.addRoleName(self.ApproximateDiameterRole, "approximate_diameter")
+ self.addRoleName(self.GuidRole, "guid")
+ self.addRoleName(self.DensityRole, "density")
+ self.addRoleName(self.DiameterRole, "diameter")
+ self.addRoleName(self.IsReadOnlyRole, "is_read_only")
+
+ from cura.CuraApplication import CuraApplication
+ self._container_registry = CuraApplication.getInstance().getContainerRegistry()
+ self._machine_manager = CuraApplication.getInstance().getMachineManager()
+ self._extruder_manager = CuraApplication.getInstance().getExtruderManager()
+ self._material_manager = CuraApplication.getInstance().getMaterialManager()
+
+ self._machine_manager.globalContainerChanged.connect(self._update)
+ self._extruder_manager.activeExtruderChanged.connect(self._update)
+ self._material_manager.materialsUpdated.connect(self._update)
+
+ self._update()
+
+ def _update(self):
+ Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
+
+ global_stack = self._machine_manager.activeMachine
+ if global_stack is None:
+ self.setItems([])
+ return
+ active_extruder_stack = self._machine_manager.activeStack
+
+ available_material_dict = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack,
+ active_extruder_stack)
+ if available_material_dict is None:
+ self.setItems([])
+ return
+
+ material_list = []
+ for root_material_id, container_node in available_material_dict.items():
+ keys_to_fetch = ("name",
+ "brand",
+ "material",
+ "color_name",
+ "color_code",
+ "description",
+ "adhesion_info",
+ "approximate_diameter",)
+
+ item = {"root_material_id": container_node.metadata["base_file"],
+ "container_node": container_node,
+ "guid": container_node.metadata["GUID"],
+ "container_id": container_node.metadata["id"],
+ "density": container_node.metadata.get("properties", {}).get("density", ""),
+ "diameter": container_node.metadata.get("properties", {}).get("diameter", ""),
+ "is_read_only": self._container_registry.isReadOnly(container_node.metadata["id"]),
+ }
+
+ for key in keys_to_fetch:
+ item[key] = container_node.metadata.get(key, "")
+
+ material_list.append(item)
+
+ material_list = sorted(material_list, key = lambda k: (k["brand"].upper(), k["name"].upper()))
+ self.setItems(material_list)
diff --git a/cura/BuildPlateModel.py b/cura/Machines/Models/MultiBuildPlateModel.py
similarity index 60%
rename from cura/BuildPlateModel.py
rename to cura/Machines/Models/MultiBuildPlateModel.py
index 73f61202c6..958e93837a 100644
--- a/cura/BuildPlateModel.py
+++ b/cura/Machines/Models/MultiBuildPlateModel.py
@@ -1,24 +1,37 @@
-from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from PyQt5.QtCore import QTimer, pyqtSignal, pyqtProperty
-from UM.Qt.ListModel import ListModel
-from UM.Scene.Selection import Selection
-from UM.Logger import Logger
from UM.Application import Application
+from UM.Scene.Selection import Selection
+from UM.Qt.ListModel import ListModel
-class BuildPlateModel(ListModel):
+#
+# This is the model for multi build plate feature.
+# This has nothing to do with the build plate types you can choose on the sidebar for a machine.
+#
+class MultiBuildPlateModel(ListModel):
+
maxBuildPlateChanged = pyqtSignal()
activeBuildPlateChanged = pyqtSignal()
selectionChanged = pyqtSignal()
- def __init__(self):
- super().__init__()
- Application.getInstance().getController().getScene().sceneChanged.connect(self._updateSelectedObjectBuildPlateNumbers)
+ def __init__(self, parent = None):
+ super().__init__(parent)
+
+ self._update_timer = QTimer()
+ self._update_timer.setInterval(100)
+ self._update_timer.setSingleShot(True)
+ self._update_timer.timeout.connect(self._updateSelectedObjectBuildPlateNumbers)
+
+ self._application = Application.getInstance()
+ self._application.getController().getScene().sceneChanged.connect(self._updateSelectedObjectBuildPlateNumbersDelayed)
Selection.selectionChanged.connect(self._updateSelectedObjectBuildPlateNumbers)
self._max_build_plate = 1 # default
self._active_build_plate = -1
- self._selection_build_plates = []
def setMaxBuildPlate(self, max_build_plate):
self._max_build_plate = max_build_plate
@@ -37,9 +50,8 @@ class BuildPlateModel(ListModel):
def activeBuildPlate(self):
return self._active_build_plate
- @staticmethod
- def createBuildPlateModel():
- return BuildPlateModel()
+ def _updateSelectedObjectBuildPlateNumbersDelayed(self, *args):
+ self._update_timer.start()
def _updateSelectedObjectBuildPlateNumbers(self, *args):
result = set()
diff --git a/cura/Machines/Models/NozzleModel.py b/cura/Machines/Models/NozzleModel.py
new file mode 100644
index 0000000000..0879998b7d
--- /dev/null
+++ b/cura/Machines/Models/NozzleModel.py
@@ -0,0 +1,61 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from PyQt5.QtCore import Qt
+
+from UM.Application import Application
+from UM.Logger import Logger
+from UM.Qt.ListModel import ListModel
+from UM.Util import parseBool
+
+
+class NozzleModel(ListModel):
+ IdRole = Qt.UserRole + 1
+ HotendNameRole = Qt.UserRole + 2
+ ContainerNodeRole = Qt.UserRole + 3
+
+ def __init__(self, parent = None):
+ super().__init__(parent)
+
+ self.addRoleName(self.IdRole, "id")
+ self.addRoleName(self.HotendNameRole, "hotend_name")
+ self.addRoleName(self.ContainerNodeRole, "container_node")
+
+ self._application = Application.getInstance()
+ self._machine_manager = self._application.getMachineManager()
+ self._variant_manager = self._application.getVariantManager()
+
+ self._machine_manager.globalContainerChanged.connect(self._update)
+ self._update()
+
+ def _update(self):
+ Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
+
+ self.items.clear()
+
+ global_stack = self._machine_manager.activeMachine
+ if global_stack is None:
+ self.setItems([])
+ return
+
+ has_variants = parseBool(global_stack.getMetaDataEntry("has_variants", False))
+ if not has_variants:
+ self.setItems([])
+ return
+
+ from cura.Machines.VariantManager import VariantType
+ variant_node_dict = self._variant_manager.getVariantNodes(global_stack, VariantType.NOZZLE)
+ if not variant_node_dict:
+ self.setItems([])
+ return
+
+ item_list = []
+ for hotend_name, container_node in sorted(variant_node_dict.items(), key = lambda i: i[0].upper()):
+ item = {"id": hotend_name,
+ "hotend_name": hotend_name,
+ "container_node": container_node
+ }
+
+ item_list.append(item)
+
+ self.setItems(item_list)
diff --git a/cura/Machines/Models/QualityManagementModel.py b/cura/Machines/Models/QualityManagementModel.py
new file mode 100644
index 0000000000..4d2b551805
--- /dev/null
+++ b/cura/Machines/Models/QualityManagementModel.py
@@ -0,0 +1,124 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from PyQt5.QtCore import Qt, pyqtSlot
+
+from UM.Qt.ListModel import ListModel
+from UM.Logger import Logger
+
+#
+# This the QML model for the quality management page.
+#
+class QualityManagementModel(ListModel):
+ NameRole = Qt.UserRole + 1
+ IsReadOnlyRole = Qt.UserRole + 2
+ QualityGroupRole = Qt.UserRole + 3
+ QualityChangesGroupRole = Qt.UserRole + 4
+
+ def __init__(self, parent = None):
+ super().__init__(parent)
+
+ self.addRoleName(self.NameRole, "name")
+ self.addRoleName(self.IsReadOnlyRole, "is_read_only")
+ self.addRoleName(self.QualityGroupRole, "quality_group")
+ self.addRoleName(self.QualityChangesGroupRole, "quality_changes_group")
+
+ from cura.CuraApplication import CuraApplication
+ self._container_registry = CuraApplication.getInstance().getContainerRegistry()
+ self._machine_manager = CuraApplication.getInstance().getMachineManager()
+ self._extruder_manager = CuraApplication.getInstance().getExtruderManager()
+ self._quality_manager = CuraApplication.getInstance().getQualityManager()
+
+ self._machine_manager.globalContainerChanged.connect(self._update)
+ self._quality_manager.qualitiesUpdated.connect(self._update)
+
+ self._update()
+
+ def _update(self):
+ Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
+
+ global_stack = self._machine_manager.activeMachine
+
+ quality_group_dict = self._quality_manager.getQualityGroups(global_stack)
+ quality_changes_group_dict = self._quality_manager.getQualityChangesGroups(global_stack)
+
+ available_quality_types = set(quality_type for quality_type, quality_group in quality_group_dict.items()
+ if quality_group.is_available)
+ if not available_quality_types and not quality_changes_group_dict:
+ # Nothing to show
+ self.setItems([])
+ return
+
+ item_list = []
+ # Create quality group items
+ for quality_group in quality_group_dict.values():
+ if not quality_group.is_available:
+ continue
+
+ item = {"name": quality_group.name,
+ "is_read_only": True,
+ "quality_group": quality_group,
+ "quality_changes_group": None}
+ item_list.append(item)
+ # Sort by quality names
+ item_list = sorted(item_list, key = lambda x: x["name"].upper())
+
+ # Create quality_changes group items
+ quality_changes_item_list = []
+ for quality_changes_group in quality_changes_group_dict.values():
+ if quality_changes_group.quality_type not in available_quality_types:
+ continue
+ quality_group = quality_group_dict[quality_changes_group.quality_type]
+ item = {"name": quality_changes_group.name,
+ "is_read_only": False,
+ "quality_group": quality_group,
+ "quality_changes_group": quality_changes_group}
+ quality_changes_item_list.append(item)
+
+ # Sort quality_changes items by names and append to the item list
+ quality_changes_item_list = sorted(quality_changes_item_list, key = lambda x: x["name"].upper())
+ item_list += quality_changes_item_list
+
+ self.setItems(item_list)
+
+ # TODO: Duplicated code here from InstanceContainersModel. Refactor and remove this later.
+ #
+ ## Gets a list of the possible file filters that the plugins have
+ # registered they can read or write. The convenience meta-filters
+ # "All Supported Types" and "All Files" are added when listing
+ # readers, but not when listing writers.
+ #
+ # \param io_type \type{str} name of the needed IO type
+ # \return A list of strings indicating file name filters for a file
+ # dialog.
+ @pyqtSlot(str, result = "QVariantList")
+ def getFileNameFilters(self, io_type):
+ from UM.i18n import i18nCatalog
+ catalog = i18nCatalog("uranium")
+ #TODO: This function should be in UM.Resources!
+ filters = []
+ all_types = []
+ for plugin_id, meta_data in self._getIOPlugins(io_type):
+ for io_plugin in meta_data[io_type]:
+ filters.append(io_plugin["description"] + " (*." + io_plugin["extension"] + ")")
+ all_types.append("*.{0}".format(io_plugin["extension"]))
+
+ if "_reader" in io_type:
+ # if we're listing readers, add the option to show all supported files as the default option
+ filters.insert(0, catalog.i18nc("@item:inlistbox", "All Supported Types ({0})", " ".join(all_types)))
+ filters.append(catalog.i18nc("@item:inlistbox", "All Files (*)")) # Also allow arbitrary files, if the user so prefers.
+ return filters
+
+ ## Gets a list of profile reader or writer plugins
+ # \return List of tuples of (plugin_id, meta_data).
+ def _getIOPlugins(self, io_type):
+ from UM.PluginRegistry import PluginRegistry
+ pr = PluginRegistry.getInstance()
+ active_plugin_ids = pr.getActivePlugins()
+
+ result = []
+ for plugin_id in active_plugin_ids:
+ meta_data = pr.getMetaData(plugin_id)
+ if io_type in meta_data:
+ result.append( (plugin_id, meta_data) )
+ return result
diff --git a/cura/Machines/Models/QualityProfilesDropDownMenuModel.py b/cura/Machines/Models/QualityProfilesDropDownMenuModel.py
new file mode 100644
index 0000000000..d8c4b668cf
--- /dev/null
+++ b/cura/Machines/Models/QualityProfilesDropDownMenuModel.py
@@ -0,0 +1,107 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from PyQt5.QtCore import Qt
+
+from UM.Application import Application
+from UM.Logger import Logger
+from UM.Qt.ListModel import ListModel
+
+from cura.Machines.QualityManager import QualityGroup
+
+
+#
+# QML Model for all built-in quality profiles. This model is used for the drop-down quality menu.
+#
+class QualityProfilesDropDownMenuModel(ListModel):
+ NameRole = Qt.UserRole + 1
+ QualityTypeRole = Qt.UserRole + 2
+ LayerHeightRole = Qt.UserRole + 3
+ LayerHeightUnitRole = Qt.UserRole + 4
+ AvailableRole = Qt.UserRole + 5
+ QualityGroupRole = Qt.UserRole + 6
+ QualityChangesGroupRole = Qt.UserRole + 7
+
+ def __init__(self, parent = None):
+ super().__init__(parent)
+
+ self.addRoleName(self.NameRole, "name")
+ self.addRoleName(self.QualityTypeRole, "quality_type")
+ self.addRoleName(self.LayerHeightRole, "layer_height")
+ self.addRoleName(self.LayerHeightUnitRole, "layer_height_unit")
+ self.addRoleName(self.AvailableRole, "available") #Whether the quality profile is available in our current nozzle + material.
+ self.addRoleName(self.QualityGroupRole, "quality_group")
+ self.addRoleName(self.QualityChangesGroupRole, "quality_changes_group")
+
+ self._application = Application.getInstance()
+ self._machine_manager = self._application.getMachineManager()
+ self._quality_manager = Application.getInstance().getQualityManager()
+
+ self._application.globalContainerStackChanged.connect(self._update)
+ self._machine_manager.activeQualityGroupChanged.connect(self._update)
+ self._machine_manager.extruderChanged.connect(self._update)
+ self._quality_manager.qualitiesUpdated.connect(self._update)
+
+ self._layer_height_unit = "" # This is cached
+
+ self._update()
+
+ def _update(self):
+ Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
+
+ global_stack = self._machine_manager.activeMachine
+ if global_stack is None:
+ self.setItems([])
+ Logger.log("d", "No active GlobalStack, set quality profile model as empty.")
+ return
+
+ # Check for material compatibility
+ if not self._machine_manager.activeMaterialsCompatible():
+ Logger.log("d", "No active material compatibility, set quality profile model as empty.")
+ self.setItems([])
+ return
+
+ quality_group_dict = self._quality_manager.getQualityGroups(global_stack)
+
+ item_list = []
+ for key in sorted(quality_group_dict):
+ quality_group = quality_group_dict[key]
+
+ layer_height = self._fetchLayerHeight(quality_group)
+
+ item = {"name": quality_group.name,
+ "quality_type": quality_group.quality_type,
+ "layer_height": layer_height,
+ "layer_height_unit": self._layer_height_unit,
+ "available": quality_group.is_available,
+ "quality_group": quality_group}
+
+ item_list.append(item)
+
+ # Sort items based on layer_height
+ item_list = sorted(item_list, key = lambda x: x["layer_height"])
+
+ self.setItems(item_list)
+
+ def _fetchLayerHeight(self, quality_group: "QualityGroup"):
+ global_stack = self._machine_manager.activeMachine
+ if not self._layer_height_unit:
+ unit = global_stack.definition.getProperty("layer_height", "unit")
+ if not unit:
+ unit = ""
+ self._layer_height_unit = unit
+
+ default_layer_height = global_stack.definition.getProperty("layer_height", "value")
+
+ # Get layer_height from the quality profile for the GlobalStack
+ container = quality_group.node_for_global.getContainer()
+
+ layer_height = default_layer_height
+ if container.hasProperty("layer_height", "value"):
+ layer_height = container.getProperty("layer_height", "value")
+ else:
+ # Look for layer_height in the GlobalStack from material -> definition
+ container = global_stack.definition
+ if container.hasProperty("layer_height", "value"):
+ layer_height = container.getProperty("layer_height", "value")
+ return float(layer_height)
diff --git a/cura/Machines/Models/QualitySettingsModel.py b/cura/Machines/Models/QualitySettingsModel.py
new file mode 100644
index 0000000000..b38f6f65c8
--- /dev/null
+++ b/cura/Machines/Models/QualitySettingsModel.py
@@ -0,0 +1,164 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from PyQt5.QtCore import pyqtProperty, pyqtSignal, Qt
+
+from UM.Application import Application
+from UM.Logger import Logger
+from UM.Qt.ListModel import ListModel
+from UM.Settings.ContainerRegistry import ContainerRegistry
+
+
+#
+# This model is used to show details settings of the selected quality in the quality management page.
+#
+class QualitySettingsModel(ListModel):
+ KeyRole = Qt.UserRole + 1
+ LabelRole = Qt.UserRole + 2
+ UnitRole = Qt.UserRole + 3
+ ProfileValueRole = Qt.UserRole + 4
+ ProfileValueSourceRole = Qt.UserRole + 5
+ UserValueRole = Qt.UserRole + 6
+ CategoryRole = Qt.UserRole + 7
+
+ GLOBAL_STACK_POSITION = -1
+
+ def __init__(self, parent = None):
+ super().__init__(parent = parent)
+
+ self.addRoleName(self.KeyRole, "key")
+ self.addRoleName(self.LabelRole, "label")
+ self.addRoleName(self.UnitRole, "unit")
+ self.addRoleName(self.ProfileValueRole, "profile_value")
+ self.addRoleName(self.ProfileValueSourceRole, "profile_value_source")
+ self.addRoleName(self.UserValueRole, "user_value")
+ self.addRoleName(self.CategoryRole, "category")
+
+ self._container_registry = ContainerRegistry.getInstance()
+ self._application = Application.getInstance()
+ self._quality_manager = self._application.getQualityManager()
+
+ self._selected_position = self.GLOBAL_STACK_POSITION #Must be either GLOBAL_STACK_POSITION or an extruder position (0, 1, etc.)
+ self._selected_quality_item = None # The selected quality in the quality management page
+ self._i18n_catalog = None
+
+ self._quality_manager.qualitiesUpdated.connect(self._update)
+
+ self._update()
+
+ selectedPositionChanged = pyqtSignal()
+ selectedQualityItemChanged = pyqtSignal()
+
+ def setSelectedPosition(self, selected_position):
+ if selected_position != self._selected_position:
+ self._selected_position = selected_position
+ self.selectedPositionChanged.emit()
+ self._update()
+
+ @pyqtProperty(int, fset = setSelectedPosition, notify = selectedPositionChanged)
+ def selectedPosition(self):
+ return self._selected_position
+
+ def setSelectedQualityItem(self, selected_quality_item):
+ if selected_quality_item != self._selected_quality_item:
+ self._selected_quality_item = selected_quality_item
+ self.selectedQualityItemChanged.emit()
+ self._update()
+
+ @pyqtProperty("QVariantMap", fset = setSelectedQualityItem, notify = selectedQualityItemChanged)
+ def selectedQualityItem(self):
+ return self._selected_quality_item
+
+ def _update(self):
+ Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
+
+ if not self._selected_quality_item:
+ self.setItems([])
+ return
+
+ items = []
+
+ global_container_stack = self._application.getGlobalContainerStack()
+ definition_container = global_container_stack.definition
+
+ quality_group = self._selected_quality_item["quality_group"]
+ quality_changes_group = self._selected_quality_item["quality_changes_group"]
+
+ if self._selected_position == self.GLOBAL_STACK_POSITION:
+ quality_node = quality_group.node_for_global
+ else:
+ quality_node = quality_group.nodes_for_extruders.get(str(self._selected_position))
+ settings_keys = quality_group.getAllKeys()
+ quality_containers = []
+ if quality_node is not None:
+ quality_containers.append(quality_node.getContainer())
+
+ # Here, if the user has selected a quality changes, then "quality_changes_group" will not be None, and we fetch
+ # the settings in that quality_changes_group.
+ if quality_changes_group is not None:
+ if self._selected_position == self.GLOBAL_STACK_POSITION:
+ quality_changes_node = quality_changes_group.node_for_global
+ else:
+ quality_changes_node = quality_changes_group.nodes_for_extruders.get(str(self._selected_position))
+ if quality_changes_node is not None: # it can be None if number of extruders are changed during runtime
+ try:
+ quality_containers.insert(0, quality_changes_node.getContainer())
+ except RuntimeError:
+ # FIXME: This is to prevent incomplete update of QualityManager
+ Logger.logException("d", "Failed to get container for quality changes node %s", quality_changes_node)
+ return
+ settings_keys.update(quality_changes_group.getAllKeys())
+
+ # We iterate over all definitions instead of settings in a quality/qualtiy_changes group is because in the GUI,
+ # the settings are grouped together by categories, and we had to go over all the definitions to figure out
+ # which setting belongs in which category.
+ current_category = ""
+ for definition in definition_container.findDefinitions():
+ if definition.type == "category":
+ current_category = definition.label
+ if self._i18n_catalog:
+ current_category = self._i18n_catalog.i18nc(definition.key + " label", definition.label)
+ continue
+
+ profile_value = None
+ profile_value_source = ""
+ for quality_container in quality_containers:
+ new_value = quality_container.getProperty(definition.key, "value")
+
+ if new_value is not None:
+ profile_value_source = quality_container.getMetaDataEntry("type")
+ profile_value = new_value
+
+ # Global tab should use resolve (if there is one)
+ if self._selected_position == self.GLOBAL_STACK_POSITION:
+ resolve_value = global_container_stack.getProperty(definition.key, "resolve")
+ if resolve_value is not None and definition.key in settings_keys:
+ profile_value = resolve_value
+
+ if profile_value is not None:
+ break
+
+ if self._selected_position == self.GLOBAL_STACK_POSITION:
+ user_value = global_container_stack.userChanges.getProperty(definition.key, "value")
+ else:
+ extruder_stack = global_container_stack.extruders[str(self._selected_position)]
+ user_value = extruder_stack.userChanges.getProperty(definition.key, "value")
+
+ if profile_value is None and user_value is None:
+ continue
+
+ label = definition.label
+ if self._i18n_catalog:
+ label = self._i18n_catalog.i18nc(definition.key + " label", label)
+
+ items.append({
+ "key": definition.key,
+ "label": label,
+ "unit": definition.unit,
+ "profile_value": "" if profile_value is None else str(profile_value), # it is for display only
+ "profile_value_source": profile_value_source,
+ "user_value": "" if user_value is None else str(user_value),
+ "category": current_category
+ })
+
+ self.setItems(items)
diff --git a/cura/Machines/Models/SettingVisibilityPresetsModel.py b/cura/Machines/Models/SettingVisibilityPresetsModel.py
new file mode 100644
index 0000000000..8880ac5ce1
--- /dev/null
+++ b/cura/Machines/Models/SettingVisibilityPresetsModel.py
@@ -0,0 +1,182 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from typing import Optional
+import os
+import urllib.parse
+from configparser import ConfigParser
+
+from PyQt5.QtCore import pyqtProperty, Qt, pyqtSignal, pyqtSlot
+
+from UM.Logger import Logger
+from UM.Qt.ListModel import ListModel
+from UM.Preferences import Preferences
+from UM.Resources import Resources
+from UM.MimeTypeDatabase import MimeTypeDatabase, MimeTypeNotFoundError
+
+from UM.i18n import i18nCatalog
+catalog = i18nCatalog("cura")
+
+
+class SettingVisibilityPresetsModel(ListModel):
+ IdRole = Qt.UserRole + 1
+ NameRole = Qt.UserRole + 2
+ SettingsRole = Qt.UserRole + 3
+
+ def __init__(self, parent = None):
+ super().__init__(parent)
+ self.addRoleName(self.IdRole, "id")
+ self.addRoleName(self.NameRole, "name")
+ self.addRoleName(self.SettingsRole, "settings")
+
+ self._populate()
+ basic_item = self.items[1]
+ basic_visibile_settings = ";".join(basic_item["settings"])
+
+ self._preferences = Preferences.getInstance()
+ # Preference to store which preset is currently selected
+ self._preferences.addPreference("cura/active_setting_visibility_preset", "basic")
+ # Preference that stores the "custom" set so it can always be restored (even after a restart)
+ self._preferences.addPreference("cura/custom_visible_settings", basic_visibile_settings)
+ self._preferences.preferenceChanged.connect(self._onPreferencesChanged)
+
+ self._active_preset_item = self._getItem(self._preferences.getValue("cura/active_setting_visibility_preset"))
+ # Initialize visible settings if it is not done yet
+ visible_settings = self._preferences.getValue("general/visible_settings")
+ if not visible_settings:
+ self._preferences.setValue("general/visible_settings", ";".join(self._active_preset_item["settings"]))
+ else:
+ self._onPreferencesChanged("general/visible_settings")
+
+ self.activePresetChanged.emit()
+
+ def _getItem(self, item_id: str) -> Optional[dict]:
+ result = None
+ for item in self.items:
+ if item["id"] == item_id:
+ result = item
+ break
+ return result
+
+ def _populate(self):
+ from cura.CuraApplication import CuraApplication
+ items = []
+ for file_path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.SettingVisibilityPreset):
+ try:
+ mime_type = MimeTypeDatabase.getMimeTypeForFile(file_path)
+ except MimeTypeNotFoundError:
+ Logger.log("e", "Could not determine mime type of file %s", file_path)
+ continue
+
+ item_id = urllib.parse.unquote_plus(mime_type.stripExtension(os.path.basename(file_path)))
+ if not os.path.isfile(file_path):
+ Logger.log("e", "[%s] is not a file", file_path)
+ continue
+
+ parser = ConfigParser(allow_no_value = True) # accept options without any value,
+ try:
+ parser.read([file_path])
+ if not parser.has_option("general", "name") or not parser.has_option("general", "weight"):
+ continue
+
+ settings = []
+ for section in parser.sections():
+ if section == 'general':
+ continue
+
+ settings.append(section)
+ for option in parser[section].keys():
+ settings.append(option)
+
+ items.append({
+ "id": item_id,
+ "name": catalog.i18nc("@action:inmenu", parser["general"]["name"]),
+ "weight": parser["general"]["weight"],
+ "settings": settings,
+ })
+
+ except Exception:
+ Logger.logException("e", "Failed to load setting preset %s", file_path)
+
+ items.sort(key = lambda k: (int(k["weight"]), k["id"]))
+ # Put "custom" at the top
+ items.insert(0, {"id": "custom",
+ "name": "Custom selection",
+ "weight": -100,
+ "settings": []})
+
+ self.setItems(items)
+
+ @pyqtSlot(str)
+ def setActivePreset(self, preset_id: str):
+ if preset_id == self._active_preset_item["id"]:
+ Logger.log("d", "Same setting visibility preset [%s] selected, do nothing.", preset_id)
+ return
+
+ preset_item = None
+ for item in self.items:
+ if item["id"] == preset_id:
+ preset_item = item
+ break
+ if preset_item is None:
+ Logger.log("w", "Tried to set active preset to unknown id [%s]", preset_id)
+ return
+
+ need_to_save_to_custom = self._active_preset_item["id"] == "custom" and preset_id != "custom"
+ if need_to_save_to_custom:
+ # Save the current visibility settings to custom
+ current_visibility_string = self._preferences.getValue("general/visible_settings")
+ if current_visibility_string:
+ self._preferences.setValue("cura/custom_visible_settings", current_visibility_string)
+
+ new_visibility_string = ";".join(preset_item["settings"])
+ if preset_id == "custom":
+ # Get settings from the stored custom data
+ new_visibility_string = self._preferences.getValue("cura/custom_visible_settings")
+ if new_visibility_string is None:
+ new_visibility_string = self._preferences.getValue("general/visible_settings")
+ self._preferences.setValue("general/visible_settings", new_visibility_string)
+
+ self._preferences.setValue("cura/active_setting_visibility_preset", preset_id)
+ self._active_preset_item = preset_item
+ self.activePresetChanged.emit()
+
+ activePresetChanged = pyqtSignal()
+
+ @pyqtProperty(str, notify = activePresetChanged)
+ def activePreset(self) -> str:
+ return self._active_preset_item["id"]
+
+ def _onPreferencesChanged(self, name: str):
+ if name != "general/visible_settings":
+ return
+
+ # Find the preset that matches with the current visible settings setup
+ visibility_string = self._preferences.getValue("general/visible_settings")
+ if not visibility_string:
+ return
+
+ visibility_set = set(visibility_string.split(";"))
+ matching_preset_item = None
+ for item in self.items:
+ if item["id"] == "custom":
+ continue
+ if set(item["settings"]) == visibility_set:
+ matching_preset_item = item
+ break
+
+ item_to_set = self._active_preset_item
+ if matching_preset_item is None:
+ # The new visibility setup is "custom" should be custom
+ if self._active_preset_item["id"] == "custom":
+ # We are already in custom, just save the settings
+ self._preferences.setValue("cura/custom_visible_settings", visibility_string)
+ else:
+ item_to_set = self.items[0] # 0 is custom
+ else:
+ item_to_set = matching_preset_item
+
+ if self._active_preset_item is None or self._active_preset_item["id"] != item_to_set["id"]:
+ self._active_preset_item = item_to_set
+ self._preferences.setValue("cura/active_setting_visibility_preset", self._active_preset_item["id"])
+ self.activePresetChanged.emit()
diff --git a/cura/Machines/Models/__init__.py b/cura/Machines/Models/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/cura/Machines/QualityChangesGroup.py b/cura/Machines/QualityChangesGroup.py
new file mode 100644
index 0000000000..ad320a7006
--- /dev/null
+++ b/cura/Machines/QualityChangesGroup.py
@@ -0,0 +1,27 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from UM.Application import Application
+
+from .QualityGroup import QualityGroup
+
+
+class QualityChangesGroup(QualityGroup):
+ def __init__(self, name: str, quality_type: str, parent = None):
+ super().__init__(name, quality_type, parent)
+ self._container_registry = Application.getInstance().getContainerRegistry()
+
+ def addNode(self, node: "QualityNode"):
+ extruder_position = node.metadata.get("position")
+ if extruder_position is None: #Then we're a global quality changes profile.
+ if self.node_for_global is not None:
+ raise RuntimeError("{group} tries to overwrite the existing node_for_global {original_global} with {new_global}".format(group = self, original_global = self.node_for_global, new_global = node))
+ self.node_for_global = node
+ else: #This is an extruder's quality changes profile.
+ if extruder_position in self.nodes_for_extruders:
+ raise RuntimeError("%s tries to overwrite the existing nodes_for_extruders position [%s] %s with %s" %
+ (self, extruder_position, self.node_for_global, node))
+ self.nodes_for_extruders[extruder_position] = node
+
+ def __str__(self) -> str:
+ return "%s[<%s>, available = %s]" % (self.__class__.__name__, self.name, self.is_available)
diff --git a/cura/Machines/QualityGroup.py b/cura/Machines/QualityGroup.py
new file mode 100644
index 0000000000..02096cfb36
--- /dev/null
+++ b/cura/Machines/QualityGroup.py
@@ -0,0 +1,50 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from typing import Dict, Optional, List
+
+from PyQt5.QtCore import QObject, pyqtSlot
+
+
+#
+# A QualityGroup represents a group of containers that must be applied to each ContainerStack when it's used.
+# Some concrete examples are Quality and QualityChanges: when we select quality type "normal", this quality type
+# must be applied to all stacks in a machine, although each stack can have different containers. Use an Ultimaker 3
+# as an example, suppose we choose quality type "normal", the actual InstanceContainers on each stack may look
+# as below:
+# GlobalStack ExtruderStack 1 ExtruderStack 2
+# quality container: um3_global_normal um3_aa04_pla_normal um3_aa04_abs_normal
+#
+# This QualityGroup is mainly used in quality and quality_changes to group the containers that can be applied to
+# a machine, so when a quality/custom quality is selected, the container can be directly applied to each stack instead
+# of looking them up again.
+#
+class QualityGroup(QObject):
+
+ def __init__(self, name: str, quality_type: str, parent = None):
+ super().__init__(parent)
+ self.name = name
+ self.node_for_global = None # type: Optional["QualityGroup"]
+ self.nodes_for_extruders = {} # type: Dict[int, "QualityGroup"]
+ self.quality_type = quality_type
+ self.is_available = False
+
+ @pyqtSlot(result = str)
+ def getName(self) -> str:
+ return self.name
+
+ def getAllKeys(self) -> set:
+ result = set()
+ for node in [self.node_for_global] + list(self.nodes_for_extruders.values()):
+ if node is None:
+ continue
+ result.update(node.getContainer().getAllKeys())
+ return result
+
+ def getAllNodes(self) -> List["QualityGroup"]:
+ result = []
+ if self.node_for_global is not None:
+ result.append(self.node_for_global)
+ for extruder_node in self.nodes_for_extruders.values():
+ result.append(extruder_node)
+ return result
diff --git a/cura/Machines/QualityManager.py b/cura/Machines/QualityManager.py
new file mode 100644
index 0000000000..8d972c9192
--- /dev/null
+++ b/cura/Machines/QualityManager.py
@@ -0,0 +1,493 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from typing import TYPE_CHECKING, Optional
+
+from PyQt5.QtCore import QObject, QTimer, pyqtSignal, pyqtSlot
+
+from UM.Application import Application
+from UM.Logger import Logger
+from UM.Util import parseBool
+from UM.Settings.InstanceContainer import InstanceContainer
+
+from cura.Settings.ExtruderStack import ExtruderStack
+
+from .QualityGroup import QualityGroup
+from .QualityNode import QualityNode
+
+if TYPE_CHECKING:
+ from UM.Settings.DefinitionContainer import DefinitionContainer
+ from cura.Settings.GlobalStack import GlobalStack
+ from .QualityChangesGroup import QualityChangesGroup
+
+
+#
+# Similar to MaterialManager, QualityManager maintains a number of maps and trees for quality profile lookup.
+# The models GUI and QML use are now only dependent on the QualityManager. That means as long as the data in
+# QualityManager gets updated correctly, the GUI models should be updated correctly too, and the same goes for GUI.
+#
+# For now, updating the lookup maps and trees here is very simple: we discard the old data completely and recreate them
+# again. This means the update is exactly the same as initialization. There are performance concerns about this approach
+# but so far the creation of the tables and maps is very fast and there is no noticeable slowness, we keep it like this
+# because it's simple.
+#
+class QualityManager(QObject):
+
+ qualitiesUpdated = pyqtSignal()
+
+ def __init__(self, container_registry, parent = None):
+ super().__init__(parent)
+ self._application = Application.getInstance()
+ self._material_manager = self._application.getMaterialManager()
+ self._container_registry = container_registry
+
+ self._empty_quality_container = self._application.empty_quality_container
+ self._empty_quality_changes_container = self._application.empty_quality_changes_container
+
+ self._machine_variant_material_quality_type_to_quality_dict = {} # for quality lookup
+ self._machine_quality_type_to_quality_changes_dict = {} # for quality_changes lookup
+
+ self._default_machine_definition_id = "fdmprinter"
+
+ self._container_registry.containerMetaDataChanged.connect(self._onContainerMetadataChanged)
+ self._container_registry.containerAdded.connect(self._onContainerMetadataChanged)
+ self._container_registry.containerRemoved.connect(self._onContainerMetadataChanged)
+
+ # When a custom quality gets added/imported, there can be more than one InstanceContainers. In those cases,
+ # we don't want to react on every container/metadata changed signal. The timer here is to buffer it a bit so
+ # we don't react too many time.
+ self._update_timer = QTimer(self)
+ self._update_timer.setInterval(300)
+ self._update_timer.setSingleShot(True)
+ self._update_timer.timeout.connect(self._updateMaps)
+
+ def initialize(self):
+ # Initialize the lookup tree for quality profiles with following structure:
+ # -> ->
+ # ->
+
+ self._machine_variant_material_quality_type_to_quality_dict = {} # for quality lookup
+ self._machine_quality_type_to_quality_changes_dict = {} # for quality_changes lookup
+
+ quality_metadata_list = self._container_registry.findContainersMetadata(type = "quality")
+ for metadata in quality_metadata_list:
+ if metadata["id"] == "empty_quality":
+ continue
+
+ definition_id = metadata["definition"]
+ quality_type = metadata["quality_type"]
+
+ root_material_id = metadata.get("material")
+ variant_name = metadata.get("variant")
+ is_global_quality = metadata.get("global_quality", False)
+ is_global_quality = is_global_quality or (root_material_id is None and variant_name is None)
+
+ # Sanity check: material+variant and is_global_quality cannot be present at the same time
+ if is_global_quality and (root_material_id or variant_name):
+ raise RuntimeError("Quality profile [%s] contains invalid data: it is a global quality but contains 'material' and 'nozzle' info." % metadata["id"])
+
+ 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]
+
+ if is_global_quality:
+ # For global qualities, save data in the machine node
+ machine_node.addQualityMetadata(quality_type, metadata)
+ continue
+
+ if variant_name is not None:
+ # If variant_name is specified in the quality/quality_changes profile, check if material is specified,
+ # too.
+ if variant_name not in machine_node.children_map:
+ machine_node.children_map[variant_name] = QualityNode()
+ variant_node = 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
+ # into the current variant node.
+ variant_node.addQualityMetadata(quality_type, metadata)
+ else:
+ # If only variant_name and material are both specified, go one level deeper: create a material node
+ # under the current variant node, and then add the quality/quality_changes metadata into the
+ # material node.
+ if root_material_id not in variant_node.children_map:
+ variant_node.children_map[root_material_id] = QualityNode()
+ material_node = variant_node.children_map[root_material_id]
+
+ material_node.addQualityMetadata(quality_type, metadata)
+
+ else:
+ # If variant_name is not specified, check if material is specified.
+ if root_material_id is not None:
+ if root_material_id not in machine_node.children_map:
+ machine_node.children_map[root_material_id] = QualityNode()
+ material_node = machine_node.children_map[root_material_id]
+
+ material_node.addQualityMetadata(quality_type, metadata)
+
+ # Initialize the lookup tree for quality_changes profiles with following structure:
+ # -> ->
+ quality_changes_metadata_list = self._container_registry.findContainersMetadata(type = "quality_changes")
+ for metadata in quality_changes_metadata_list:
+ if metadata["id"] == "empty_quality_changes":
+ continue
+
+ machine_definition_id = metadata["definition"]
+ quality_type = metadata["quality_type"]
+
+ if machine_definition_id not in self._machine_quality_type_to_quality_changes_dict:
+ self._machine_quality_type_to_quality_changes_dict[machine_definition_id] = QualityNode()
+ machine_node = self._machine_quality_type_to_quality_changes_dict[machine_definition_id]
+ machine_node.addQualityChangesMetadata(quality_type, metadata)
+
+ Logger.log("d", "Lookup tables updated.")
+ self.qualitiesUpdated.emit()
+
+ def _updateMaps(self):
+ self.initialize()
+
+ def _onContainerMetadataChanged(self, container):
+ self._onContainerChanged(container)
+
+ def _onContainerChanged(self, container):
+ container_type = container.getMetaDataEntry("type")
+ if container_type not in ("quality", "quality_changes"):
+ return
+
+ # update the cache table
+ self._update_timer.start()
+
+ # Updates the given quality groups' availabilities according to which extruders are being used/ enabled.
+ def _updateQualityGroupsAvailability(self, machine: "GlobalStack", quality_group_list):
+ used_extruders = set()
+ for i in range(machine.getProperty("machine_extruder_count", "value")):
+ if machine.extruders[str(i)].isEnabled:
+ used_extruders.add(str(i))
+
+ # Update the "is_available" flag for each quality group.
+ for quality_group in quality_group_list:
+ is_available = True
+ if quality_group.node_for_global is None:
+ is_available = False
+ if is_available:
+ for position in used_extruders:
+ if position not in quality_group.nodes_for_extruders:
+ is_available = False
+ break
+
+ quality_group.is_available = is_available
+
+ # Returns a dict of "custom profile name" -> QualityChangesGroup
+ def getQualityChangesGroups(self, machine: "GlobalStack") -> dict:
+ machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
+
+ machine_node = self._machine_quality_type_to_quality_changes_dict.get(machine_definition_id)
+ if not machine_node:
+ Logger.log("i", "Cannot find node for machine def [%s] in QualityChanges lookup table", machine_definition_id)
+ return dict()
+
+ # Update availability for each QualityChangesGroup:
+ # A custom profile is always available as long as the quality_type it's based on is available
+ quality_group_dict = self.getQualityGroups(machine)
+ available_quality_type_list = [qt for qt, qg in quality_group_dict.items() if qg.is_available]
+
+ # Iterate over all quality_types in the machine node
+ quality_changes_group_dict = dict()
+ for quality_type, quality_changes_node in machine_node.quality_type_map.items():
+ for quality_changes_name, quality_changes_group in quality_changes_node.children_map.items():
+ quality_changes_group_dict[quality_changes_name] = quality_changes_group
+ quality_changes_group.is_available = quality_type in available_quality_type_list
+
+ return quality_changes_group_dict
+
+ #
+ # Gets all quality groups for the given machine. Both available and none available ones will be included.
+ # It returns a dictionary with "quality_type"s as keys and "QualityGroup"s as values.
+ # Whether a QualityGroup is available can be unknown via the field QualityGroup.is_available.
+ # For more details, see QualityGroup.
+ #
+ def getQualityGroups(self, machine: "GlobalStack") -> dict:
+ machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
+
+ # This determines if we should only get the global qualities for the global stack and skip the global qualities for the extruder stacks
+ has_variant_materials = parseBool(machine.getMetaDataEntry("has_variant_materials", False))
+
+ # To find the quality container for the GlobalStack, check in the following fall-back manner:
+ # (1) the machine-specific node
+ # (2) the generic node
+ machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(machine_definition_id)
+ default_machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(self._default_machine_definition_id)
+ nodes_to_check = [machine_node, default_machine_node]
+
+ # Iterate over all quality_types in the machine node
+ quality_group_dict = {}
+ for node in nodes_to_check:
+ if node and node.quality_type_map:
+ # Only include global qualities
+ if has_variant_materials:
+ quality_node = list(node.quality_type_map.values())[0]
+ is_global_quality = parseBool(quality_node.metadata.get("global_quality", False))
+ if not is_global_quality:
+ continue
+
+ for quality_type, quality_node in node.quality_type_map.items():
+ quality_group = QualityGroup(quality_node.metadata["name"], quality_type)
+ quality_group.node_for_global = quality_node
+ quality_group_dict[quality_type] = quality_group
+ break
+
+ # Iterate over all extruders to find quality containers for each extruder
+ for position, extruder in machine.extruders.items():
+ variant_name = None
+ if extruder.variant.getId() != "empty_variant":
+ variant_name = extruder.variant.getName()
+
+ # This is a list of root material IDs to use for searching for suitable quality profiles.
+ # The root material IDs in this list are in prioritized order.
+ root_material_id_list = []
+ has_material = False # flag indicating whether this extruder has a material assigned
+ if extruder.material.getId() != "empty_material":
+ has_material = True
+ root_material_id = extruder.material.getMetaDataEntry("base_file")
+ # Convert possible generic_pla_175 -> generic_pla
+ root_material_id = self._material_manager.getRootMaterialIDWithoutDiameter(root_material_id)
+ root_material_id_list.append(root_material_id)
+
+ # Also try to get the fallback material
+ material_type = extruder.material.getMetaDataEntry("material")
+ fallback_root_material_id = self._material_manager.getFallbackMaterialIdByMaterialType(material_type)
+ if fallback_root_material_id:
+ root_material_id_list.append(fallback_root_material_id)
+
+ # Here we construct a list of nodes we want to look for qualities with the highest priority first.
+ # The use case is that, when we look for qualities for a machine, we first want to search in the following
+ # order:
+ # 1. machine-variant-and-material-specific qualities if exist
+ # 2. machine-variant-specific qualities if exist
+ # 3. machine-material-specific qualities if exist
+ # 4. machine-specific qualities if exist
+ # 5. generic qualities if exist
+ # Each points above can be represented as a node in the lookup tree, so here we simply put those nodes into
+ # the list with priorities as the order. Later, we just need to loop over each node in this list and fetch
+ # qualities from there.
+ nodes_to_check = []
+
+ if variant_name:
+ # In this case, we have both a specific variant and a specific material
+ variant_node = machine_node.getChildNode(variant_name)
+ if variant_node and has_material:
+ for root_material_id in root_material_id_list:
+ material_node = variant_node.getChildNode(root_material_id)
+ if material_node:
+ nodes_to_check.append(material_node)
+ break
+ nodes_to_check.append(variant_node)
+
+ # In this case, we only have a specific material but NOT a variant
+ if has_material:
+ for root_material_id in root_material_id_list:
+ material_node = machine_node.getChildNode(root_material_id)
+ if material_node:
+ nodes_to_check.append(material_node)
+ break
+
+ nodes_to_check += [machine_node, default_machine_node]
+ for node in nodes_to_check:
+ if node and node.quality_type_map:
+ if has_variant_materials:
+ # Only include variant qualities; skip non global qualities
+ quality_node = list(node.quality_type_map.values())[0]
+ is_global_quality = parseBool(quality_node.metadata.get("global_quality", False))
+ if is_global_quality:
+ continue
+
+ for quality_type, quality_node in node.quality_type_map.items():
+ if quality_type not in quality_group_dict:
+ quality_group = QualityGroup(quality_node.metadata["name"], quality_type)
+ quality_group_dict[quality_type] = quality_group
+
+ quality_group = quality_group_dict[quality_type]
+ quality_group.nodes_for_extruders[position] = quality_node
+ break
+
+ # Update availabilities for each quality group
+ self._updateQualityGroupsAvailability(machine, quality_group_dict.values())
+
+ return quality_group_dict
+
+ def getQualityGroupsForMachineDefinition(self, machine: "GlobalStack") -> dict:
+ machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
+
+ # To find the quality container for the GlobalStack, check in the following fall-back manner:
+ # (1) the machine-specific node
+ # (2) the generic node
+ machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(machine_definition_id)
+ default_machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(
+ self._default_machine_definition_id)
+ nodes_to_check = [machine_node, default_machine_node]
+
+ # Iterate over all quality_types in the machine node
+ quality_group_dict = dict()
+ for node in nodes_to_check:
+ if node and node.quality_type_map:
+ for quality_type, quality_node in node.quality_type_map.items():
+ quality_group = QualityGroup(quality_node.metadata["name"], quality_type)
+ quality_group.node_for_global = quality_node
+ quality_group_dict[quality_type] = quality_group
+ break
+
+ return quality_group_dict
+
+ #
+ # Methods for GUI
+ #
+
+ #
+ # Remove the given quality changes group.
+ #
+ @pyqtSlot(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"])
+
+ #
+ # Rename a set of quality changes containers. Returns the new name.
+ #
+ @pyqtSlot(QObject, str, result = str)
+ def renameQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup", new_name: str) -> str:
+ Logger.log("i", "Renaming QualityChangesGroup[%s] to [%s]", quality_changes_group.name, new_name)
+ if new_name == quality_changes_group.name:
+ Logger.log("i", "QualityChangesGroup name [%s] unchanged.", quality_changes_group.name)
+ return new_name
+
+ new_name = self._container_registry.uniqueName(new_name)
+ for node in quality_changes_group.getAllNodes():
+ node.getContainer().setName(new_name)
+
+ quality_changes_group.name = new_name
+
+ self._application.getMachineManager().activeQualityChanged.emit()
+ self._application.getMachineManager().activeQualityGroupChanged.emit()
+
+ return new_name
+
+ #
+ # Duplicates the given quality.
+ #
+ @pyqtSlot(str, "QVariantMap")
+ def duplicateQualityChanges(self, quality_changes_name, quality_model_item):
+ global_stack = self._application.getGlobalContainerStack()
+ if not global_stack:
+ Logger.log("i", "No active global stack, cannot duplicate quality changes.")
+ return
+
+ quality_group = quality_model_item["quality_group"]
+ quality_changes_group = quality_model_item["quality_changes_group"]
+ if quality_changes_group is None:
+ # create global quality changes only
+ new_quality_changes = self._createQualityChanges(quality_group.quality_type, quality_changes_name,
+ global_stack, None)
+ self._container_registry.addContainer(new_quality_changes)
+ else:
+ new_name = self._container_registry.uniqueName(quality_changes_name)
+ for node in quality_changes_group.getAllNodes():
+ container = node.getContainer()
+ new_id = self._container_registry.uniqueName(container.getId())
+ self._container_registry.addContainer(container.duplicate(new_id, new_name))
+
+ ## Create quality changes containers from the user containers in the active stacks.
+ #
+ # This will go through the global and extruder stacks and create quality_changes containers from
+ # the user containers in each stack. These then replace the quality_changes containers in the
+ # stack and clear the user settings.
+ @pyqtSlot(str)
+ def createQualityChanges(self, base_name):
+ machine_manager = Application.getInstance().getMachineManager()
+
+ global_stack = machine_manager.activeMachine
+ if not global_stack:
+ return
+
+ active_quality_name = machine_manager.activeQualityOrQualityChangesName
+ if active_quality_name == "":
+ Logger.log("w", "No quality container found in stack %s, cannot create profile", global_stack.getId())
+ return
+
+ machine_manager.blurSettings.emit()
+ if base_name is None or base_name == "":
+ base_name = active_quality_name
+ unique_name = self._container_registry.uniqueName(base_name)
+
+ # Go through the active stacks and create quality_changes containers from the user containers.
+ stack_list = [global_stack] + list(global_stack.extruders.values())
+ for stack in stack_list:
+ user_container = stack.userChanges
+ quality_container = stack.quality
+ quality_changes_container = stack.qualityChanges
+ if not quality_container or not quality_changes_container:
+ Logger.log("w", "No quality or quality changes container found in stack %s, ignoring it", stack.getId())
+ continue
+
+ quality_type = quality_container.getMetaDataEntry("quality_type")
+ extruder_stack = None
+ if isinstance(stack, ExtruderStack):
+ extruder_stack = stack
+ new_changes = self._createQualityChanges(quality_type, unique_name, global_stack, extruder_stack)
+ from cura.Settings.ContainerManager import ContainerManager
+ ContainerManager.getInstance()._performMerge(new_changes, quality_changes_container, clear_settings = False)
+ ContainerManager.getInstance()._performMerge(new_changes, user_container)
+
+ self._container_registry.addContainer(new_changes)
+
+ #
+ # Create a quality changes container with the given setup.
+ #
+ def _createQualityChanges(self, quality_type: str, new_name: str, machine: "GlobalStack",
+ extruder_stack: Optional["ExtruderStack"]) -> "InstanceContainer":
+ base_id = machine.definition.getId() if extruder_stack is None else extruder_stack.getId()
+ new_id = base_id + "_" + new_name
+ new_id = new_id.lower().replace(" ", "_")
+ new_id = self._container_registry.uniqueName(new_id)
+
+ # Create a new quality_changes container for the quality.
+ quality_changes = InstanceContainer(new_id)
+ quality_changes.setName(new_name)
+ quality_changes.addMetaDataEntry("type", "quality_changes")
+ quality_changes.addMetaDataEntry("quality_type", quality_type)
+
+ # If we are creating a container for an extruder, ensure we add that to the container
+ if extruder_stack is not None:
+ quality_changes.addMetaDataEntry("position", extruder_stack.getMetaDataEntry("position"))
+
+ # If the machine specifies qualities should be filtered, ensure we match the current criteria.
+ machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
+ quality_changes.setDefinition(machine_definition_id)
+
+ quality_changes.addMetaDataEntry("setting_version", self._application.SettingVersion)
+ return quality_changes
+
+
+#
+# Gets the machine definition ID that can be used to search for Quality containers that are suitable for the given
+# machine. The rule is as follows:
+# 1. By default, the machine definition ID for quality container search will be "fdmprinter", which is the generic
+# machine.
+# 2. If a machine has its own machine quality (with "has_machine_quality = True"), we should use the given machine's
+# own machine definition ID for quality search.
+# Example: for an Ultimaker 3, the definition ID should be "ultimaker3".
+# 3. When condition (2) is met, AND the machine has "quality_definition" defined in its definition file, then the
+# definition ID specified in "quality_definition" should be used.
+# Example: for an Ultimaker 3 Extended, it has "quality_definition = ultimaker3". This means Ultimaker 3 Extended
+# shares the same set of qualities profiles as Ultimaker 3.
+#
+def getMachineDefinitionIDForQualitySearch(machine_definition: "DefinitionContainer",
+ default_definition_id: str = "fdmprinter") -> str:
+ machine_definition_id = default_definition_id
+ if parseBool(machine_definition.getMetaDataEntry("has_machine_quality", False)):
+ # Only use the machine's own quality definition ID if this machine has machine quality.
+ machine_definition_id = machine_definition.getMetaDataEntry("quality_definition")
+ if machine_definition_id is None:
+ machine_definition_id = machine_definition.getId()
+
+ return machine_definition_id
diff --git a/cura/Machines/QualityNode.py b/cura/Machines/QualityNode.py
new file mode 100644
index 0000000000..a30e219da3
--- /dev/null
+++ b/cura/Machines/QualityNode.py
@@ -0,0 +1,35 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from typing import Optional
+
+from .ContainerNode import ContainerNode
+from .QualityChangesGroup import QualityChangesGroup
+
+
+#
+# QualityNode is used for BOTH quality and quality_changes containers.
+#
+class QualityNode(ContainerNode):
+
+ def __init__(self, metadata: Optional[dict] = None):
+ super().__init__(metadata = metadata)
+ self.quality_type_map = {} # quality_type -> QualityNode for InstanceContainer
+
+ def addQualityMetadata(self, quality_type: str, metadata: dict):
+ if quality_type not in self.quality_type_map:
+ self.quality_type_map[quality_type] = QualityNode(metadata)
+
+ def getQualityNode(self, quality_type: str) -> Optional["QualityNode"]:
+ return self.quality_type_map.get(quality_type)
+
+ def addQualityChangesMetadata(self, quality_type: str, metadata: dict):
+ if quality_type not in self.quality_type_map:
+ self.quality_type_map[quality_type] = QualityNode()
+ quality_type_node = self.quality_type_map[quality_type]
+
+ name = metadata["name"]
+ 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))
diff --git a/cura/Machines/VariantManager.py b/cura/Machines/VariantManager.py
new file mode 100644
index 0000000000..4e033e054e
--- /dev/null
+++ b/cura/Machines/VariantManager.py
@@ -0,0 +1,145 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from enum import Enum
+from collections import OrderedDict
+from typing import Optional, TYPE_CHECKING
+
+from UM.Logger import Logger
+from UM.Settings.ContainerRegistry import ContainerRegistry
+from UM.Util import parseBool
+
+from cura.Machines.ContainerNode import ContainerNode
+from cura.Settings.GlobalStack import GlobalStack
+
+if TYPE_CHECKING:
+ from UM.Settings.DefinitionContainer import DefinitionContainer
+
+
+class VariantType(Enum):
+ BUILD_PLATE = "buildplate"
+ NOZZLE = "nozzle"
+
+
+ALL_VARIANT_TYPES = (VariantType.BUILD_PLATE, VariantType.NOZZLE)
+
+
+#
+# VariantManager is THE place to look for a specific variant. It maintains two variant lookup tables with the following
+# structure:
+#
+# [machine_definition_id] -> [variant_type] -> [variant_name] -> ContainerNode(metadata / container)
+# Example: "ultimaker3" -> "buildplate" -> "Glass" (if present) -> ContainerNode
+# -> ...
+# -> "nozzle" -> "AA 0.4"
+# -> "BB 0.8"
+# -> ...
+#
+# [machine_definition_id] -> [machine_buildplate_type] -> ContainerNode(metadata / container)
+# Example: "ultimaker3" -> "glass" (this is different from the variant name) -> ContainerNode
+#
+# Note that the "container" field is not loaded in the beginning because it would defeat the purpose of lazy-loading.
+# A container is loaded when getVariant() is called to load a variant InstanceContainer.
+#
+class VariantManager:
+
+ def __init__(self, container_registry):
+ self._container_registry = container_registry # type: ContainerRegistry
+
+ self._machine_to_variant_dict_map = dict() # ->
+ self._machine_to_buildplate_dict_map = dict()
+
+ self._exclude_variant_id_list = ["empty_variant"]
+
+ #
+ # Initializes the VariantManager including:
+ # - initializing the variant lookup table based on the metadata in ContainerRegistry.
+ #
+ def initialize(self):
+ self._machine_to_variant_dict_map = OrderedDict()
+ self._machine_to_buildplate_dict_map = OrderedDict()
+
+ # Cache all variants from the container registry to a variant map for better searching and organization.
+ variant_metadata_list = self._container_registry.findContainersMetadata(type = "variant")
+ for variant_metadata in variant_metadata_list:
+ if variant_metadata["id"] in self._exclude_variant_id_list:
+ Logger.log("d", "Exclude variant [%s]", variant_metadata["id"])
+ continue
+
+ variant_name = variant_metadata["name"]
+ variant_definition = variant_metadata["definition"]
+ if variant_definition not in self._machine_to_variant_dict_map:
+ self._machine_to_variant_dict_map[variant_definition] = OrderedDict()
+ for variant_type in ALL_VARIANT_TYPES:
+ self._machine_to_variant_dict_map[variant_definition][variant_type] = dict()
+
+ variant_type = variant_metadata["hardware_type"]
+ variant_type = VariantType(variant_type)
+ variant_dict = self._machine_to_variant_dict_map[variant_definition][variant_type]
+ if variant_name in variant_dict:
+ # ERROR: duplicated variant name.
+ raise RuntimeError("Found duplicated variant name [%s], type [%s] for machine [%s]" %
+ (variant_name, variant_type, variant_definition))
+
+ variant_dict[variant_name] = ContainerNode(metadata = variant_metadata)
+
+ # If the variant is a buildplate then fill also the buildplate map
+ if variant_type == VariantType.BUILD_PLATE:
+ if variant_definition not in self._machine_to_buildplate_dict_map:
+ self._machine_to_buildplate_dict_map[variant_definition] = OrderedDict()
+
+ variant_container = self._container_registry.findContainers(type = "variant", id = variant_metadata["id"])
+ if not variant_container:
+ # ERROR: not variant container. This should never happen
+ raise RuntimeError("Not variant found [%s], type [%s] for machine [%s]" %
+ (variant_name, variant_type, variant_definition))
+ buildplate_type = variant_container[0].getProperty("machine_buildplate_type", "value")
+ if buildplate_type not in self._machine_to_buildplate_dict_map[variant_definition]:
+ self._machine_to_variant_dict_map[variant_definition][buildplate_type] = dict()
+
+ self._machine_to_buildplate_dict_map[variant_definition][buildplate_type] = variant_dict[variant_name]
+
+ #
+ # Gets the variant InstanceContainer with the given information.
+ # Almost the same as getVariantMetadata() except that this returns an InstanceContainer if present.
+ #
+ def getVariantNode(self, machine_definition_id: str, variant_name: str,
+ variant_type: Optional["VariantType"] = None) -> Optional["ContainerNode"]:
+ if variant_type is None:
+ variant_node = None
+ variant_type_dict = self._machine_to_variant_dict_map[machine_definition_id]
+ for variant_dict in variant_type_dict.values():
+ if variant_name in variant_dict:
+ variant_node = variant_dict[variant_name]
+ break
+ return variant_node
+ return self._machine_to_variant_dict_map[machine_definition_id].get(variant_type, {}).get(variant_name)
+
+ def getVariantNodes(self, machine: "GlobalStack",
+ variant_type: Optional["VariantType"] = None) -> dict:
+ machine_definition_id = machine.definition.getId()
+ return self._machine_to_variant_dict_map.get(machine_definition_id, {}).get(variant_type, {})
+
+ #
+ # Gets the default variant for the given machine definition.
+ #
+ def getDefaultVariantNode(self, machine_definition: "DefinitionContainer",
+ variant_type: VariantType) -> Optional["ContainerNode"]:
+ machine_definition_id = machine_definition.getId()
+ preferred_variant_name = None
+ if variant_type == VariantType.BUILD_PLATE:
+ if parseBool(machine_definition.getMetaDataEntry("has_variant_buildplates", False)):
+ preferred_variant_name = machine_definition.getMetaDataEntry("preferred_variant_buildplate_name")
+ else:
+ if parseBool(machine_definition.getMetaDataEntry("has_variants", False)):
+ preferred_variant_name = machine_definition.getMetaDataEntry("preferred_variant_name")
+
+ node = None
+ if preferred_variant_name:
+ node = self.getVariantNode(machine_definition_id, preferred_variant_name, variant_type)
+ return node
+
+ def getBuildplateVariantNode(self, machine_definition_id: str, buildplate_type: str) -> Optional["ContainerNode"]:
+ if machine_definition_id in self._machine_to_buildplate_dict_map:
+ return self._machine_to_buildplate_dict_map[machine_definition_id].get(buildplate_type)
+ return None
diff --git a/cura/Machines/__init__.py b/cura/Machines/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py
index 441d4c96c3..b9f37ec6f8 100644
--- a/cura/MultiplyObjectsJob.py
+++ b/cura/MultiplyObjectsJob.py
@@ -33,6 +33,7 @@ class MultiplyObjectsJob(Job):
root = scene.getRoot()
arranger = Arrange.create(scene_root=root)
nodes = []
+
for node in self._objects:
# If object is part of a group, multiply group
current_node = node
@@ -49,18 +50,20 @@ class MultiplyObjectsJob(Job):
for i in range(self._count):
# We do place the nodes one by one, as we want to yield in between.
if not node_too_big:
- node, solution_found = arranger.findNodePlacement(current_node, offset_shape_arr, hull_shape_arr)
+ new_node, solution_found = arranger.findNodePlacement(current_node, offset_shape_arr, hull_shape_arr)
if node_too_big or not solution_found:
found_solution_for_all = False
- new_location = node.getPosition()
+ new_location = new_node.getPosition()
new_location = new_location.set(z = 100 - i * 20)
- node.setPosition(new_location)
+ new_node.setPosition(new_location)
# Same build plate
build_plate_number = current_node.callDecoration("getBuildPlateNumber")
- node.callDecoration("setBuildPlateNumber", build_plate_number)
+ new_node.callDecoration("setBuildPlateNumber", build_plate_number)
+ for child in new_node.getChildren():
+ child.callDecoration("setBuildPlateNumber", build_plate_number)
- nodes.append(node)
+ nodes.append(new_node)
current_progress += 1
status_message.setProgress((current_progress / total_progress) * 100)
Job.yieldThread()
diff --git a/cura/ObjectsModel.py b/cura/ObjectsModel.py
index 1516b3ee33..cfe4320e28 100644
--- a/cura/ObjectsModel.py
+++ b/cura/ObjectsModel.py
@@ -1,3 +1,8 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from PyQt5.QtCore import QTimer
+
from UM.Application import Application
from UM.Qt.ListModel import ListModel
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
@@ -8,13 +13,19 @@ from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
+
## Keep track of all objects in the project
class ObjectsModel(ListModel):
def __init__(self):
super().__init__()
- Application.getInstance().getController().getScene().sceneChanged.connect(self._update)
- Preferences.getInstance().preferenceChanged.connect(self._update)
+ Application.getInstance().getController().getScene().sceneChanged.connect(self._updateDelayed)
+ Preferences.getInstance().preferenceChanged.connect(self._updateDelayed)
+
+ self._update_timer = QTimer()
+ self._update_timer.setInterval(100)
+ self._update_timer.setSingleShot(True)
+ self._update_timer.timeout.connect(self._update)
self._build_plate_number = -1
@@ -22,6 +33,9 @@ class ObjectsModel(ListModel):
self._build_plate_number = nr
self._update()
+ def _updateDelayed(self, *args):
+ self._update_timer.start()
+
def _update(self, *args):
nodes = []
filter_current_build_plate = Preferences.getInstance().getValue("view/filter_current_build_plate")
@@ -46,10 +60,15 @@ class ObjectsModel(ListModel):
name = catalog.i18nc("@label", "Group #{group_nr}").format(group_nr = str(group_nr))
group_nr += 1
+ if hasattr(node, "isOutsideBuildArea"):
+ is_outside_build_area = node.isOutsideBuildArea()
+ else:
+ is_outside_build_area = False
+
nodes.append({
"name": name,
"isSelected": Selection.isSelected(node),
- "isOutsideBuildArea": node.isOutsideBuildArea(),
+ "isOutsideBuildArea": is_outside_build_area,
"buildPlateNumber": node_build_plate_number,
"node": node
})
diff --git a/cura/PickingPass.py b/cura/PickingPass.py
new file mode 100644
index 0000000000..2a1abe8f63
--- /dev/null
+++ b/cura/PickingPass.py
@@ -0,0 +1,69 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+from UM.Application import Application
+from UM.Math.Vector import Vector
+from UM.Resources import Resources
+
+from UM.View.RenderPass import RenderPass
+from UM.View.GL.OpenGL import OpenGL
+from UM.View.RenderBatch import RenderBatch
+
+from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
+
+
+## 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):
+ super().__init__("picking", width, height)
+
+ self._renderer = Application.getInstance().getRenderer()
+
+ self._shader = None
+ self._scene = Application.getInstance().getController().getScene()
+
+ def render(self) -> None:
+ if not self._shader:
+ self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "camera_distance.shader"))
+
+ width, height = self.getSize()
+ self._gl.glViewport(0, 0, width, height)
+ self._gl.glClearColor(1.0, 1.0, 1.0, 0.0)
+ self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)
+
+ # Create a new batch to be rendered
+ batch = RenderBatch(self._shader)
+
+ # Fill up the batch with objects that can be sliced. `
+ for node in DepthFirstIterator(self._scene.getRoot()):
+ if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible():
+ batch.addItem(node.getWorldTransformation(), node.getMeshData())
+
+ self.bind()
+ batch.render(self._scene.getActiveCamera())
+ self.release()
+
+ ## Get the distance in mm from the camera to at a certain pixel coordinate.
+ def getPickedDepth(self, x: int, y: int) -> float:
+ output = self.getOutput()
+
+ window_size = self._renderer.getWindowSize()
+
+ px = (0.5 + x / 2.0) * window_size[0]
+ py = (0.5 + y / 2.0) * window_size[1]
+
+ if px < 0 or px > (output.width() - 1) or py < 0 or py > (output.height() - 1):
+ return -1
+
+ distance = output.pixel(px, py) # distance in micron, from in r, g & b channels
+ distance = (distance & 0x00ffffff) / 1000. # drop the alpha channel and covert to mm
+ return distance
+
+ ## Get the world coordinates of a picked point
+ def getPickedPosition(self, x: int, y: int) -> Vector:
+ distance = self.getPickedDepth(x, y)
+ ray = self._scene.getActiveCamera().getRay(x, y)
+
+ return ray.getPointAlongRay(distance)
diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py
index 69890178e4..1a5d6ef837 100755
--- a/cura/PlatformPhysics.py
+++ b/cura/PlatformPhysics.py
@@ -40,6 +40,8 @@ class PlatformPhysics:
Preferences.getInstance().addPreference("physics/automatic_drop_down", True)
def _onSceneChanged(self, source):
+ if not source.getMeshData():
+ return
self._change_timer.start()
def _onChangeTimerFinished(self):
@@ -69,7 +71,7 @@ class PlatformPhysics:
# Move it downwards if bottom is above platform
move_vector = Vector()
- if Preferences.getInstance().getValue("physics/automatic_drop_down") and not (node.getParent() and node.getParent().callDecoration("isGroup")) and node.isEnabled(): #If an object is grouped, don't move it down
+ if Preferences.getInstance().getValue("physics/automatic_drop_down") and not (node.getParent() and node.getParent().callDecoration("isGroup") or node.getParent() != root) and node.isEnabled(): #If an object is grouped, don't move it down
z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0
move_vector = move_vector.set(y = -bbox.bottom + z_offset)
diff --git a/cura/PreviewPass.py b/cura/PreviewPass.py
index af42b59b78..de21a5dc86 100644
--- a/cura/PreviewPass.py
+++ b/cura/PreviewPass.py
@@ -17,6 +17,18 @@ MYPY = False
if MYPY:
from UM.Scene.Camera import Camera
+
+# Make color brighter by normalizing it (maximum factor 2.5 brighter)
+# color_list is a list of 4 elements: [r, g, b, a], each element is a float 0..1
+def prettier_color(color_list):
+ maximum = max(color_list[:3])
+ if maximum > 0:
+ factor = min(1 / maximum, 2.5)
+ else:
+ factor = 1.0
+ return [min(i * factor, 1.0) for i in color_list]
+
+
## A render pass subclass that renders slicable objects with default parameters.
# It uses the active camera by default, but it can be overridden to use a different camera.
#
@@ -41,6 +53,9 @@ class PreviewPass(RenderPass):
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)
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)
@@ -52,7 +67,7 @@ class PreviewPass(RenderPass):
for node in DepthFirstIterator(self._scene.getRoot()):
if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible():
uniforms = {}
- uniforms["diffuse_color"] = node.getDiffuseColor()
+ uniforms["diffuse_color"] = prettier_color(node.getDiffuseColor())
batch.addItem(node.getWorldTransformation(), node.getMeshData(), uniforms = uniforms)
self.bind()
diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py
index cfd1e3fd3b..6d73489448 100644
--- a/cura/PrintInformation.py
+++ b/cura/PrintInformation.py
@@ -1,27 +1,22 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
-from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty
-from UM.FlameProfiler import pyqtSlot
+from typing import Dict
+import math
+import os.path
+import unicodedata
+import json
+import re # To create abbreviations for printer names.
+
+from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot
from UM.Application import Application
from UM.Logger import Logger
from UM.Qt.Duration import Duration
from UM.Preferences import Preferences
from UM.Scene.SceneNode import SceneNode
-from UM.Settings.ContainerRegistry import ContainerRegistry
-from cura.Scene.CuraSceneNode import CuraSceneNode
-
-from cura.Settings.ExtruderManager import ExtruderManager
-from typing import Dict
-
-import math
-import os.path
-import unicodedata
-import json
-import re #To create abbreviations for printer names.
-
from UM.i18n import i18nCatalog
+
catalog = i18nCatalog("cura")
## A class for processing and calculating minimum, current and maximum print time as well as managing the job name
@@ -76,16 +71,19 @@ class PrintInformation(QObject):
self._active_build_plate = 0
self._initVariablesWithBuildPlate(self._active_build_plate)
- Application.getInstance().globalContainerStackChanged.connect(self._updateJobName)
- Application.getInstance().fileLoaded.connect(self.setBaseName)
- Application.getInstance().getBuildPlateModel().activeBuildPlateChanged.connect(self._onActiveBuildPlateChanged)
- Application.getInstance().workspaceLoaded.connect(self.setProjectName)
+ self._application = Application.getInstance()
+ self._multi_build_plate_model = self._application.getMultiBuildPlateModel()
+
+ self._application.globalContainerStackChanged.connect(self._updateJobName)
+ self._application.globalContainerStackChanged.connect(self.setToZeroPrintInformation)
+ self._application.fileLoaded.connect(self.setBaseName)
+ self._application.workspaceLoaded.connect(self.setProjectName)
+ self._multi_build_plate_model.activeBuildPlateChanged.connect(self._onActiveBuildPlateChanged)
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
- self._active_material_container = None
- Application.getInstance().getMachineManager().activeMaterialChanged.connect(self._onActiveMaterialChanged)
- self._onActiveMaterialChanged()
+ self._application.getMachineManager().rootMaterialChanged.connect(self._onActiveMaterialsChanged)
+ self._onActiveMaterialsChanged()
self._material_amounts = []
@@ -110,7 +108,6 @@ class PrintInformation(QObject):
self._print_time_message_values = {}
-
def _initPrintTimeMessageValues(self, build_plate_number):
# Full fill message values using keys from _print_time_message_translations
self._print_time_message_values[build_plate_number] = {}
@@ -201,11 +198,10 @@ class PrintInformation(QObject):
self._current_print_time[build_plate_number].setDuration(total_estimated_time)
def _calculateInformation(self, build_plate_number):
- if Application.getInstance().getGlobalContainerStack() is None:
+ global_stack = Application.getInstance().getGlobalContainerStack()
+ if global_stack is None:
return
- # Material amount is sent as an amount of mm^3, so calculate length from that
- radius = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2
self._material_lengths[build_plate_number] = []
self._material_weights[build_plate_number] = []
self._material_costs[build_plate_number] = []
@@ -213,18 +209,17 @@ class PrintInformation(QObject):
material_preference_values = json.loads(Preferences.getInstance().getValue("cura/material_settings"))
- extruder_stacks = list(ExtruderManager.getInstance().getMachineExtruders(Application.getInstance().getGlobalContainerStack().getId()))
- for index, amount in enumerate(self._material_amounts):
+ extruder_stacks = global_stack.extruders
+ for position, extruder_stack in extruder_stacks.items():
+ index = int(position)
+ if index >= len(self._material_amounts):
+ continue
+ amount = self._material_amounts[index]
## Find the right extruder stack. As the list isn't sorted because it's a annoying generator, we do some
# list comprehension filtering to solve this for us.
- material = None
- if extruder_stacks: # Multi extrusion machine
- extruder_stack = [extruder for extruder in extruder_stacks if extruder.getMetaDataEntry("position") == str(index)][0]
- density = extruder_stack.getMetaDataEntry("properties", {}).get("density", 0)
- material = extruder_stack.findContainer({"type": "material"})
- else: # Machine with no extruder stacks
- density = Application.getInstance().getGlobalContainerStack().getMetaDataEntry("properties", {}).get("density", 0)
- material = Application.getInstance().getGlobalContainerStack().findContainer({"type": "material"})
+ density = extruder_stack.getMetaDataEntry("properties", {}).get("density", 0)
+ material = extruder_stack.findContainer({"type": "material"})
+ radius = extruder_stack.getProperty("material_diameter", "value") / 2
weight = float(amount) * float(density) / 1000
cost = 0
@@ -243,6 +238,7 @@ class PrintInformation(QObject):
else:
cost = 0
+ # Material amount is sent as an amount of mm^3, so calculate length from that
if radius != 0:
length = round((amount / (math.pi * radius ** 2)) / 1000, 2)
else:
@@ -261,25 +257,11 @@ class PrintInformation(QObject):
if preference != "cura/material_settings":
return
- for build_plate_number in range(Application.getInstance().getBuildPlateModel().maxBuildPlate + 1):
+ for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1):
self._calculateInformation(build_plate_number)
- def _onActiveMaterialChanged(self):
- if self._active_material_container:
- try:
- self._active_material_container.metaDataChanged.disconnect(self._onMaterialMetaDataChanged)
- except TypeError: #pyQtSignal gives a TypeError when disconnecting from something that is already disconnected.
- pass
-
- active_material_id = Application.getInstance().getMachineManager().activeMaterialId
- active_material_containers = ContainerRegistry.getInstance().findInstanceContainers(id = active_material_id)
-
- if active_material_containers:
- self._active_material_container = active_material_containers[0]
- self._active_material_container.metaDataChanged.connect(self._onMaterialMetaDataChanged)
-
def _onActiveBuildPlateChanged(self):
- new_active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
+ new_active_build_plate = self._multi_build_plate_model.activeBuildPlate
if new_active_build_plate != self._active_build_plate:
self._active_build_plate = new_active_build_plate
@@ -291,8 +273,8 @@ class PrintInformation(QObject):
self.materialNamesChanged.emit()
self.currentPrintTimeChanged.emit()
- def _onMaterialMetaDataChanged(self, *args, **kwargs):
- for build_plate_number in range(Application.getInstance().getBuildPlateModel().maxBuildPlate + 1):
+ def _onActiveMaterialsChanged(self, *args, **kwargs):
+ for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1):
self._calculateInformation(build_plate_number)
@pyqtSlot(str)
@@ -327,16 +309,13 @@ class PrintInformation(QObject):
self.jobNameChanged.emit()
- @pyqtProperty(str)
- def baseName(self):
- return self._base_name
-
@pyqtSlot(str)
def setProjectName(self, name):
self.setBaseName(name, is_project_file = True)
- @pyqtSlot(str)
- def setBaseName(self, base_name, is_project_file = False):
+ baseNameChanged = pyqtSignal()
+
+ def setBaseName(self, base_name: str, is_project_file: bool = False):
# Ensure that we don't use entire path but only filename
name = os.path.basename(base_name)
@@ -354,6 +333,9 @@ class PrintInformation(QObject):
self._base_name = name
self._updateJobName()
+ @pyqtProperty(str, fset = setBaseName, notify = baseNameChanged)
+ def baseName(self):
+ return self._base_name
## Created an acronymn-like abbreviated machine name from the currently active machine name
# Called each time the global stack is switched
@@ -362,10 +344,10 @@ class PrintInformation(QObject):
if not global_container_stack:
self._abbr_machine = ""
return
- active_machine_type_id = global_container_stack.definition.getId()
+ active_machine_type_name = global_container_stack.definition.getName()
abbr_machine = ""
- for word in re.findall(r"[\w']+", active_machine_type_id):
+ for word in re.findall(r"[\w']+", active_machine_type_name):
if word.lower() == "ultimaker":
abbr_machine += "UM"
elif word.isdigit():
@@ -397,7 +379,9 @@ class PrintInformation(QObject):
return result
# Simulate message with zero time duration
- def setToZeroPrintInformation(self, build_plate):
+ def setToZeroPrintInformation(self, build_plate = None):
+ if build_plate is None:
+ build_plate = self._active_build_plate
# Construct the 0-time message
temp_message = {}
@@ -411,7 +395,6 @@ class PrintInformation(QObject):
## Listen to scene changes to check if we need to reset the print information
def _onSceneChanged(self, scene_node):
-
# Ignore any changes that are not related to sliceable objects
if not isinstance(scene_node, SceneNode)\
or not scene_node.callDecoration("isSliceable")\
diff --git a/cura/PrinterOutput/ConfigurationModel.py b/cura/PrinterOutput/ConfigurationModel.py
new file mode 100644
index 0000000000..c03d968b9e
--- /dev/null
+++ b/cura/PrinterOutput/ConfigurationModel.py
@@ -0,0 +1,81 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from PyQt5.QtCore import pyqtProperty, QObject, pyqtSignal
+from typing import List
+
+MYPY = False
+if MYPY:
+ from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
+
+
+class ConfigurationModel(QObject):
+
+ configurationChanged = pyqtSignal()
+
+ def __init__(self):
+ super().__init__()
+ self._printer_type = None
+ self._extruder_configurations = [] # type: List[ExtruderConfigurationModel]
+ self._buildplate_configuration = None
+
+ def setPrinterType(self, printer_type):
+ self._printer_type = printer_type
+
+ @pyqtProperty(str, fset = setPrinterType, notify = configurationChanged)
+ def printerType(self):
+ return self._printer_type
+
+ def setExtruderConfigurations(self, extruder_configurations):
+ self._extruder_configurations = extruder_configurations
+
+ @pyqtProperty("QVariantList", fset = setExtruderConfigurations, notify = configurationChanged)
+ def extruderConfigurations(self):
+ return self._extruder_configurations
+
+ def setBuildplateConfiguration(self, buildplate_configuration):
+ self._buildplate_configuration = buildplate_configuration
+
+ @pyqtProperty(str, fset = setBuildplateConfiguration, notify = configurationChanged)
+ def buildplateConfiguration(self):
+ return self._buildplate_configuration
+
+ ## This method is intended to indicate whether the configuration is valid or not.
+ # The method checks if the mandatory fields are or not set
+ def isValid(self):
+ if not self._extruder_configurations:
+ return False
+ for configuration in self._extruder_configurations:
+ if configuration is None:
+ return False
+ return self._printer_type is not None
+
+ def __str__(self):
+ message_chunks = []
+ message_chunks.append("Printer type: " + self._printer_type)
+ message_chunks.append("Extruders: [")
+ for configuration in self._extruder_configurations:
+ message_chunks.append(" " + str(configuration))
+ message_chunks.append("]")
+ if self._buildplate_configuration is not None:
+ message_chunks.append("Buildplate: " + self._buildplate_configuration)
+
+ return "\n".join(message_chunks)
+
+ def __eq__(self, other):
+ return hash(self) == hash(other)
+
+ ## The hash function is used to compare and create unique sets. The configuration is unique if the configuration
+ # of the extruders is unique (the order of the extruders matters), and the type and buildplate is the same.
+ def __hash__(self):
+ extruder_hash = hash(0)
+ first_extruder = None
+ for configuration in self._extruder_configurations:
+ extruder_hash ^= hash(configuration)
+ if configuration.position == 0:
+ first_extruder = configuration
+ # To ensure the correct order of the extruders, we add an "and" operation using the first extruder hash value
+ if first_extruder:
+ extruder_hash &= hash(first_extruder)
+
+ return hash(self._printer_type) ^ extruder_hash ^ hash(self._buildplate_configuration)
\ No newline at end of file
diff --git a/cura/PrinterOutput/ExtruderConfigurationModel.py b/cura/PrinterOutput/ExtruderConfigurationModel.py
new file mode 100644
index 0000000000..bc7f1a7c07
--- /dev/null
+++ b/cura/PrinterOutput/ExtruderConfigurationModel.py
@@ -0,0 +1,59 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from PyQt5.QtCore import pyqtProperty, QObject, pyqtSignal
+
+
+class ExtruderConfigurationModel(QObject):
+
+ extruderConfigurationChanged = pyqtSignal()
+
+ def __init__(self):
+ super().__init__()
+ self._position = -1
+ self._material = None
+ self._hotend_id = None
+
+ def setPosition(self, position):
+ self._position = position
+
+ @pyqtProperty(int, fset = setPosition, notify = extruderConfigurationChanged)
+ def position(self):
+ return self._position
+
+ def setMaterial(self, material):
+ self._material = material
+
+ @pyqtProperty(QObject, fset = setMaterial, notify = extruderConfigurationChanged)
+ def material(self):
+ return self._material
+
+ def setHotendID(self, hotend_id):
+ self._hotend_id = hotend_id
+
+ @pyqtProperty(str, fset = setHotendID, notify = extruderConfigurationChanged)
+ def hotendID(self):
+ return self._hotend_id
+
+ ## This method is intended to indicate whether the configuration is valid or not.
+ # The method checks if the mandatory fields are or not set
+ # At this moment is always valid since we allow to have empty material and variants.
+ def isValid(self):
+ return True
+
+ def __str__(self):
+ message_chunks = []
+ message_chunks.append("Position: " + str(self._position))
+ message_chunks.append("-")
+ message_chunks.append("Material: " + self.material.type if self.material else "empty")
+ message_chunks.append("-")
+ message_chunks.append("HotendID: " + self.hotendID if self.hotendID else "empty")
+ return " ".join(message_chunks)
+
+ def __eq__(self, other):
+ return hash(self) == hash(other)
+
+ # Calculating a hash function using the position of the extruder, the material GUID and the hotend id to check if is
+ # unique within a set
+ def __hash__(self):
+ return hash(self._position) ^ (hash(self._material.guid) if self._material is not None else hash(0)) ^ hash(self._hotend_id)
\ No newline at end of file
diff --git a/cura/PrinterOutput/ExtruderOuputModel.py b/cura/PrinterOutput/ExtruderOutputModel.py
similarity index 52%
rename from cura/PrinterOutput/ExtruderOuputModel.py
rename to cura/PrinterOutput/ExtruderOutputModel.py
index b0be6cbbe4..75b9cc98ac 100644
--- a/cura/PrinterOutput/ExtruderOuputModel.py
+++ b/cura/PrinterOutput/ExtruderOutputModel.py
@@ -1,8 +1,8 @@
-# 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, pyqtSlot
-from UM.Logger import Logger
+from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, pyqtSlot
+from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
from typing import Optional
@@ -17,14 +17,34 @@ class ExtruderOutputModel(QObject):
targetHotendTemperatureChanged = pyqtSignal()
hotendTemperatureChanged = pyqtSignal()
activeMaterialChanged = pyqtSignal()
+ extruderConfigurationChanged = pyqtSignal()
+ isPreheatingChanged = pyqtSignal()
- def __init__(self, printer: "PrinterOutputModel", parent=None):
+ def __init__(self, printer: "PrinterOutputModel", position, parent=None):
super().__init__(parent)
self._printer = printer
+ self._position = position
self._target_hotend_temperature = 0
self._hotend_temperature = 0
self._hotend_id = ""
self._active_material = None # type: Optional[MaterialOutputModel]
+ self._extruder_configuration = ExtruderConfigurationModel()
+ self._extruder_configuration.position = self._position
+
+ self._is_preheating = False
+
+ def getPrinter(self):
+ return self._printer
+
+ def getPosition(self):
+ return self._position
+
+ # Does the printer support pre-heating the bed at all
+ @pyqtProperty(bool, constant=True)
+ def canPreHeatHotends(self):
+ if self._printer:
+ return self._printer.canPreHeatHotends
+ return False
@pyqtProperty(QObject, notify = activeMaterialChanged)
def activeMaterial(self) -> "MaterialOutputModel":
@@ -33,7 +53,9 @@ class ExtruderOutputModel(QObject):
def updateActiveMaterial(self, material: Optional["MaterialOutputModel"]):
if self._active_material != material:
self._active_material = material
+ self._extruder_configuration.material = self._active_material
self.activeMaterialChanged.emit()
+ self.extruderConfigurationChanged.emit()
## Update the hotend temperature. This only changes it locally.
def updateHotendTemperature(self, temperature: float):
@@ -56,7 +78,7 @@ class ExtruderOutputModel(QObject):
def targetHotendTemperature(self) -> float:
return self._target_hotend_temperature
- @pyqtProperty(float, notify=hotendTemperatureChanged)
+ @pyqtProperty(float, notify = hotendTemperatureChanged)
def hotendTemperature(self) -> float:
return self._hotend_temperature
@@ -67,4 +89,34 @@ class ExtruderOutputModel(QObject):
def updateHotendID(self, id: str):
if self._hotend_id != id:
self._hotend_id = id
+ self._extruder_configuration.hotendID = self._hotend_id
self.hotendIDChanged.emit()
+ self.extruderConfigurationChanged.emit()
+
+ @pyqtProperty(QObject, notify = extruderConfigurationChanged)
+ def extruderConfiguration(self):
+ if self._extruder_configuration.isValid():
+ return self._extruder_configuration
+ return None
+
+ def updateIsPreheating(self, pre_heating):
+ if self._is_preheating != pre_heating:
+ self._is_preheating = pre_heating
+ self.isPreheatingChanged.emit()
+
+ @pyqtProperty(bool, notify=isPreheatingChanged)
+ def isPreheating(self):
+ return self._is_preheating
+
+ ## Pre-heats the extruder before printer.
+ #
+ # \param temperature The temperature to heat the extruder to, in degrees
+ # Celsius.
+ # \param duration How long the bed should stay warm, in seconds.
+ @pyqtSlot(float, float)
+ def preheatHotend(self, temperature, duration):
+ self._printer._controller.preheatHotend(self, temperature, duration)
+
+ @pyqtSlot()
+ def cancelPreheatHotend(self):
+ self._printer._controller.cancelPreheatHotend(self)
\ No newline at end of file
diff --git a/cura/PrinterOutput/GenericOutputController.py b/cura/PrinterOutput/GenericOutputController.py
new file mode 100644
index 0000000000..470848c208
--- /dev/null
+++ b/cura/PrinterOutput/GenericOutputController.py
@@ -0,0 +1,154 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
+from PyQt5.QtCore import QTimer
+
+MYPY = False
+if MYPY:
+ from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
+ from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
+
+
+class GenericOutputController(PrinterOutputController):
+ def __init__(self, output_device):
+ super().__init__(output_device)
+
+ self._preheat_bed_timer = QTimer()
+ self._preheat_bed_timer.setSingleShot(True)
+ self._preheat_bed_timer.timeout.connect(self._onPreheatBedTimerFinished)
+ self._preheat_printer = None
+
+ self._preheat_hotends_timer = QTimer()
+ self._preheat_hotends_timer.setSingleShot(True)
+ self._preheat_hotends_timer.timeout.connect(self._onPreheatHotendsTimerFinished)
+ self._preheat_hotends = set()
+
+ self._output_device.printersChanged.connect(self._onPrintersChanged)
+ self._active_printer = None
+
+ def _onPrintersChanged(self):
+ if self._active_printer:
+ self._active_printer.stateChanged.disconnect(self._onPrinterStateChanged)
+ self._active_printer.targetBedTemperatureChanged.disconnect(self._onTargetBedTemperatureChanged)
+ for extruder in self._active_printer.extruders:
+ extruder.targetHotendTemperatureChanged.disconnect(self._onTargetHotendTemperatureChanged)
+
+ self._active_printer = self._output_device.activePrinter
+ if self._active_printer:
+ self._active_printer.stateChanged.connect(self._onPrinterStateChanged)
+ self._active_printer.targetBedTemperatureChanged.connect(self._onTargetBedTemperatureChanged)
+ for extruder in self._active_printer.extruders:
+ extruder.targetHotendTemperatureChanged.connect(self._onTargetHotendTemperatureChanged)
+
+ def _onPrinterStateChanged(self):
+ if self._active_printer.state != "idle":
+ if self._preheat_bed_timer.isActive():
+ self._preheat_bed_timer.stop()
+ self._preheat_printer.updateIsPreheating(False)
+ if self._preheat_hotends_timer.isActive():
+ self._preheat_hotends_timer.stop()
+ for extruder in self._preheat_hotends:
+ extruder.updateIsPreheating(False)
+ self._preheat_hotends = set()
+
+ def moveHead(self, printer: "PrinterOutputModel", x, y, z, speed):
+ self._output_device.sendCommand("G91")
+ self._output_device.sendCommand("G0 X%s Y%s Z%s F%s" % (x, y, z, speed))
+ self._output_device.sendCommand("G90")
+
+ def homeHead(self, printer):
+ self._output_device.sendCommand("G28 X")
+ self._output_device.sendCommand("G28 Y")
+
+ def homeBed(self, printer):
+ self._output_device.sendCommand("G28 Z")
+
+ def sendRawCommand(self, printer: "PrinterOutputModel", command: str):
+ self._output_device.sendCommand(command)
+
+ def setJobState(self, job: "PrintJobOutputModel", state: str):
+ if state == "pause":
+ self._output_device.pausePrint()
+ job.updateState("paused")
+ elif state == "print":
+ self._output_device.resumePrint()
+ job.updateState("printing")
+ elif state == "abort":
+ self._output_device.cancelPrint()
+ pass
+
+ def setTargetBedTemperature(self, printer: "PrinterOutputModel", temperature: int):
+ self._output_device.sendCommand("M140 S%s" % temperature)
+
+ def _onTargetBedTemperatureChanged(self):
+ if self._preheat_bed_timer.isActive() and self._preheat_printer.targetBedTemperature == 0:
+ self._preheat_bed_timer.stop()
+ self._preheat_printer.updateIsPreheating(False)
+
+ def preheatBed(self, printer: "PrinterOutputModel", temperature, duration):
+ try:
+ temperature = round(temperature) # The API doesn't allow floating point.
+ duration = round(duration)
+ except ValueError:
+ return # Got invalid values, can't pre-heat.
+
+ self.setTargetBedTemperature(printer, temperature=temperature)
+ self._preheat_bed_timer.setInterval(duration * 1000)
+ self._preheat_bed_timer.start()
+ self._preheat_printer = printer
+ printer.updateIsPreheating(True)
+
+ def cancelPreheatBed(self, printer: "PrinterOutputModel"):
+ self.setTargetBedTemperature(printer, temperature=0)
+ self._preheat_bed_timer.stop()
+ printer.updateIsPreheating(False)
+
+ def _onPreheatBedTimerFinished(self):
+ self.setTargetBedTemperature(self._preheat_printer, 0)
+ self._preheat_printer.updateIsPreheating(False)
+
+ def setTargetHotendTemperature(self, printer: "PrinterOutputModel", position: int, temperature: int):
+ self._output_device.sendCommand("M104 S%s T%s" % (temperature, position))
+
+ def _onTargetHotendTemperatureChanged(self):
+ if not self._preheat_hotends_timer.isActive():
+ return
+
+ for extruder in self._active_printer.extruders:
+ if extruder in self._preheat_hotends and extruder.targetHotendTemperature == 0:
+ extruder.updateIsPreheating(False)
+ self._preheat_hotends.remove(extruder)
+ if not self._preheat_hotends:
+ self._preheat_hotends_timer.stop()
+
+ def preheatHotend(self, extruder: "ExtruderOutputModel", temperature, duration):
+ position = extruder.getPosition()
+ number_of_extruders = len(extruder.getPrinter().extruders)
+ if position >= number_of_extruders:
+ return # Got invalid extruder nr, can't pre-heat.
+
+ try:
+ temperature = round(temperature) # The API doesn't allow floating point.
+ duration = round(duration)
+ except ValueError:
+ return # Got invalid values, can't pre-heat.
+
+ self.setTargetHotendTemperature(extruder.getPrinter(), position, temperature=temperature)
+ self._preheat_hotends_timer.setInterval(duration * 1000)
+ self._preheat_hotends_timer.start()
+ self._preheat_hotends.add(extruder)
+ extruder.updateIsPreheating(True)
+
+ def cancelPreheatHotend(self, extruder: "ExtruderOutputModel"):
+ self.setTargetHotendTemperature(extruder.getPrinter(), extruder.getPosition(), temperature=0)
+ if extruder in self._preheat_hotends:
+ extruder.updateIsPreheating(False)
+ self._preheat_hotends.remove(extruder)
+ if not self._preheat_hotends and self._preheat_hotends_timer.isActive():
+ self._preheat_hotends_timer.stop()
+
+ def _onPreheatHotendsTimerFinished(self):
+ for extruder in self._preheat_hotends:
+ self.setTargetHotendTemperature(extruder.getPrinter(), extruder.getPosition(), 0)
+ self._preheat_hotends = set()
diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py
index a7b7edc636..9da57a812e 100644
--- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py
+++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py
@@ -1,8 +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.
from UM.Application import Application
from UM.Logger import Logger
+from cura.CuraApplication import CuraApplication
from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState
@@ -55,6 +56,17 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
self._connection_state_before_timeout = None # type: Optional[ConnectionState]
+ printer_type = self._properties.get(b"machine", b"").decode("utf-8")
+ printer_type_identifiers = {
+ "9066": "ultimaker3",
+ "9511": "ultimaker3_extended"
+ }
+ self._printer_type = "Unknown"
+ for key, value in printer_type_identifiers.items():
+ if printer_type.startswith(key):
+ self._printer_type = value
+ break
+
def requestWrite(self, nodes, file_name=None, filter_by_machine=False, file_handler=None, **kwargs) -> None:
raise NotImplementedError("requestWrite needs to be implemented")
@@ -219,6 +231,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
reply.uploadProgress.connect(onProgress)
self._registerOnFinishedCallback(reply, onFinished)
+ return reply
+
def postForm(self, target: str, header_data: str, body_data: bytes, onFinished: Optional[Callable[[Any, QNetworkReply], None]], onProgress: Callable = None) -> None:
post_part = QHttpPart()
post_part.setHeader(QNetworkRequest.ContentDispositionHeader, header_data)
@@ -240,6 +254,9 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
self._last_manager_create_time = time()
self._manager.authenticationRequired.connect(self._onAuthenticationRequired)
+ machine_manager = CuraApplication.getInstance().getMachineManager()
+ machine_manager.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
@@ -298,6 +315,10 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
def firmwareVersion(self) -> str:
return self._properties.get(b"firmware_version", b"").decode("utf-8")
+ @pyqtProperty(str, constant=True)
+ def printerType(self) -> str:
+ return self._printer_type
+
## IPadress of this printer
@pyqtProperty(str, constant=True)
def ipAddress(self) -> str:
diff --git a/cura/PrinterOutput/PrinterOutputController.py b/cura/PrinterOutput/PrinterOutputController.py
index 86ca10e2d3..58c6ef05a7 100644
--- a/cura/PrinterOutput/PrinterOutputController.py
+++ b/cura/PrinterOutput/PrinterOutputController.py
@@ -6,7 +6,7 @@ from UM.Logger import Logger
MYPY = False
if MYPY:
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
- from cura.PrinterOutput.ExtruderOuputModel import ExtruderOuputModel
+ from cura.PrinterOutput.ExtruderOutputModel import ExtruderOutputModel
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
@@ -15,10 +15,12 @@ class PrinterOutputController:
self.can_pause = True
self.can_abort = True
self.can_pre_heat_bed = True
+ self.can_pre_heat_hotends = True
+ self.can_send_raw_gcode = True
self.can_control_manually = True
self._output_device = output_device
- def setTargetHotendTemperature(self, printer: "PrinterOutputModel", extruder: "ExtruderOuputModel", temperature: int):
+ def setTargetHotendTemperature(self, printer: "PrinterOutputModel", extruder: "ExtruderOutputModel", temperature: int):
Logger.log("w", "Set target hotend temperature not implemented in controller")
def setTargetBedTemperature(self, printer: "PrinterOutputModel", temperature: int):
@@ -33,14 +35,23 @@ class PrinterOutputController:
def preheatBed(self, printer: "PrinterOutputModel", temperature, duration):
Logger.log("w", "Preheat bed not implemented in controller")
+ def cancelPreheatHotend(self, extruder: "ExtruderOutputModel"):
+ Logger.log("w", "Cancel preheat hotend not implemented in controller")
+
+ def preheatHotend(self, extruder: "ExtruderOutputModel", temperature, duration):
+ Logger.log("w", "Preheat hotend not implemented in controller")
+
def setHeadPosition(self, printer: "PrinterOutputModel", x, y, z, speed):
Logger.log("w", "Set head position not implemented in controller")
def moveHead(self, printer: "PrinterOutputModel", x, y, z, speed):
Logger.log("w", "Move head not implemented in controller")
- def homeBed(self, printer):
+ def homeBed(self, printer: "PrinterOutputModel"):
Logger.log("w", "Home bed not implemented in controller")
- def homeHead(self, printer):
- Logger.log("w", "Home head not implemented in controller")
\ No newline at end of file
+ def homeHead(self, printer: "PrinterOutputModel"):
+ Logger.log("w", "Home head not implemented in controller")
+
+ def sendRawCommand(self, printer: "PrinterOutputModel", command: str):
+ Logger.log("w", "Custom command not implemented in controller")
diff --git a/cura/PrinterOutput/PrinterOutputModel.py b/cura/PrinterOutput/PrinterOutputModel.py
index 8234989519..928a882c8c 100644
--- a/cura/PrinterOutput/PrinterOutputModel.py
+++ b/cura/PrinterOutput/PrinterOutputModel.py
@@ -1,11 +1,11 @@
-# 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, pyqtSlot
-from UM.Logger import Logger
-from typing import Optional, List
+from typing import Optional
from UM.Math.Vector import Vector
-from cura.PrinterOutput.ExtruderOuputModel import ExtruderOutputModel
+from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
+from cura.PrinterOutput.ExtruderOutputModel import ExtruderOutputModel
MYPY = False
if MYPY:
@@ -22,8 +22,10 @@ class PrinterOutputModel(QObject):
nameChanged = pyqtSignal()
headPositionChanged = pyqtSignal()
keyChanged = pyqtSignal()
- typeChanged = pyqtSignal()
+ printerTypeChanged = pyqtSignal()
+ buildplateChanged = pyqtSignal()
cameraChanged = pyqtSignal()
+ configurationChanged = pyqtSignal()
def __init__(self, output_controller: "PrinterOutputController", number_of_extruders: int = 1, parent=None, firmware_version = ""):
super().__init__(parent)
@@ -32,13 +34,18 @@ class PrinterOutputModel(QObject):
self._name = ""
self._key = "" # Unique identifier
self._controller = output_controller
- self._extruders = [ExtruderOutputModel(printer=self) for i in range(number_of_extruders)]
+ self._extruders = [ExtruderOutputModel(printer = self, position = i) for i in range(number_of_extruders)]
+ self._printer_configuration = ConfigurationModel() # Indicates the current configuration setup in this printer
self._head_position = Vector(0, 0, 0)
self._active_print_job = None # type: Optional[PrintJobOutputModel]
self._firmware_version = firmware_version
self._printer_state = "unknown"
self._is_preheating = False
- self._type = ""
+ self._printer_type = ""
+ self._buildplate_name = None
+ # Update the printer configuration every time any of the extruders changes its configuration
+ for extruder in self._extruders:
+ extruder.extruderConfigurationChanged.connect(self._updateExtruderConfiguration)
self._camera = None
@@ -64,14 +71,27 @@ class PrinterOutputModel(QObject):
def camera(self):
return self._camera
- @pyqtProperty(str, notify = typeChanged)
+ @pyqtProperty(str, notify = printerTypeChanged)
def type(self):
- return self._type
+ return self._printer_type
- def updateType(self, type):
- if self._type != type:
- self._type = type
- self.typeChanged.emit()
+ def updateType(self, printer_type):
+ if self._printer_type != printer_type:
+ self._printer_type = printer_type
+ self._printer_configuration.printerType = self._printer_type
+ self.printerTypeChanged.emit()
+ self.configurationChanged.emit()
+
+ @pyqtProperty(str, notify = buildplateChanged)
+ def buildplate(self):
+ return self._buildplate_name
+
+ def updateBuildplateName(self, buildplate_name):
+ if self._buildplate_name != buildplate_name:
+ self._buildplate_name = buildplate_name
+ self._printer_configuration.buildplateConfiguration = self._buildplate_name
+ self.buildplateChanged.emit()
+ self.configurationChanged.emit()
@pyqtProperty(str, notify=keyChanged)
def key(self):
@@ -90,6 +110,10 @@ class PrinterOutputModel(QObject):
def homeBed(self):
self._controller.homeBed(self)
+ @pyqtSlot(str)
+ def sendRawCommand(self, command: str):
+ self._controller.sendRawCommand(self, command)
+
@pyqtProperty("QVariantList", constant = True)
def extruders(self):
return self._extruders
@@ -103,32 +127,32 @@ class PrinterOutputModel(QObject):
self._head_position = Vector(x, y, z)
self.headPositionChanged.emit()
- @pyqtProperty("long", "long", "long")
- @pyqtProperty("long", "long", "long", "long")
+ @pyqtProperty(float, float, float)
+ @pyqtProperty(float, float, float, float)
def setHeadPosition(self, x, y, z, speed = 3000):
self.updateHeadPosition(x, y, z)
self._controller.setHeadPosition(self, x, y, z, speed)
- @pyqtProperty("long")
- @pyqtProperty("long", "long")
+ @pyqtProperty(float)
+ @pyqtProperty(float, float)
def setHeadX(self, x, speed = 3000):
self.updateHeadPosition(x, self._head_position.y, self._head_position.z)
self._controller.setHeadPosition(self, x, self._head_position.y, self._head_position.z, speed)
- @pyqtProperty("long")
- @pyqtProperty("long", "long")
+ @pyqtProperty(float)
+ @pyqtProperty(float, float)
def setHeadY(self, y, speed = 3000):
self.updateHeadPosition(self._head_position.x, y, self._head_position.z)
self._controller.setHeadPosition(self, self._head_position.x, y, self._head_position.z, speed)
- @pyqtProperty("long")
- @pyqtProperty("long", "long")
+ @pyqtProperty(float)
+ @pyqtProperty(float, float)
def setHeadZ(self, z, speed = 3000):
self.updateHeadPosition(self._head_position.x, self._head_position.y, z)
self._controller.setHeadPosition(self, self._head_position.x, self._head_position.y, z, speed)
- @pyqtSlot("long", "long", "long")
- @pyqtSlot("long", "long", "long", "long")
+ @pyqtSlot(float, float, float)
+ @pyqtSlot(float, float, float, float)
def moveHead(self, x = 0, y = 0, z = 0, speed = 3000):
self._controller.moveHead(self, x, y, z, speed)
@@ -218,6 +242,20 @@ class PrinterOutputModel(QObject):
return self._controller.can_pre_heat_bed
return False
+ # Does the printer support pre-heating the bed at all
+ @pyqtProperty(bool, constant=True)
+ def canPreHeatHotends(self):
+ if self._controller:
+ return self._controller.can_pre_heat_hotends
+ return False
+
+ # Does the printer support sending raw G-code at all
+ @pyqtProperty(bool, constant=True)
+ def canSendRawGcode(self):
+ if self._controller:
+ return self._controller.can_send_raw_gcode
+ return False
+
# Does the printer support pause at all
@pyqtProperty(bool, constant=True)
def canPause(self):
@@ -238,3 +276,14 @@ class PrinterOutputModel(QObject):
if self._controller:
return self._controller.can_control_manually
return False
+
+ # Returns the configuration (material, variant and buildplate) of the current printer
+ @pyqtProperty(QObject, notify = configurationChanged)
+ def printerConfiguration(self):
+ if self._printer_configuration.isValid():
+ return self._printer_configuration
+ return None
+
+ def _updateExtruderConfiguration(self):
+ self._printer_configuration.extruderConfigurations = [extruder.extruderConfiguration for extruder in self._extruders]
+ self.configurationChanged.emit()
diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py
index 9e603b83ae..4d6ddb8dfa 100644
--- a/cura/PrinterOutputDevice.py
+++ b/cura/PrinterOutputDevice.py
@@ -1,12 +1,11 @@
-# Copyright (c) 2017 Ultimaker B.V.
+# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM.i18n import i18nCatalog
from UM.OutputDevice.OutputDevice import OutputDevice
-from PyQt5.QtCore import pyqtProperty, QObject, QTimer, pyqtSignal
+from PyQt5.QtCore import pyqtProperty, QObject, QTimer, pyqtSignal, QVariant
from PyQt5.QtWidgets import QMessageBox
-
from UM.Logger import Logger
from UM.Signal import signalemitter
from UM.Application import Application
@@ -17,6 +16,7 @@ from typing import List, Optional
MYPY = False
if MYPY:
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
+ from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
i18n_catalog = i18nCatalog("cura")
@@ -44,10 +44,14 @@ class PrinterOutputDevice(QObject, OutputDevice):
# Signal to indicate that the info text about the connection has changed.
connectionTextChanged = pyqtSignal()
+ # Signal to indicate that the configuration of one of the printers has changed.
+ uniqueConfigurationsChanged = pyqtSignal()
+
def __init__(self, device_id, parent = 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
@@ -69,6 +73,8 @@ class PrinterOutputDevice(QObject, OutputDevice):
self._address = ""
self._connection_text = ""
+ self.printersChanged.connect(self._onPrintersChanged)
+ Application.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._updateUniqueConfigurations)
@pyqtProperty(str, notify = connectionTextChanged)
def address(self):
@@ -175,6 +181,23 @@ class PrinterOutputDevice(QObject, OutputDevice):
self.acceptsCommandsChanged.emit()
+ # Returns the unique configurations of the printers within this output device
+ @pyqtProperty("QVariantList", notify = uniqueConfigurationsChanged)
+ def uniqueConfigurations(self):
+ return self._unique_configurations
+
+ def _updateUniqueConfigurations(self):
+ 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):
+ for printer in self._printers:
+ printer.configurationChanged.connect(self._updateUniqueConfigurations)
+
+ # At this point there may be non-updated configurations
+ self._updateUniqueConfigurations()
+
## The current processing state of the backend.
class ConnectionState(IntEnum):
diff --git a/cura/ProfileReader.py b/cura/ProfileReader.py
index d4600ed99f..460fce823e 100644
--- a/cura/ProfileReader.py
+++ b/cura/ProfileReader.py
@@ -3,6 +3,13 @@
from UM.PluginObject import PluginObject
+
+# Exception when there is no profile to import from a given files.
+# Note that this should not be treated as an exception but as an information instead.
+class NoProfileException(Exception):
+ pass
+
+
## A type of plug-ins that reads profiles from a file.
#
# The profile is then stored as instance container of the type user profile.
@@ -14,4 +21,4 @@ class ProfileReader(PluginObject):
#
# \return \type{Profile|Profile[]} The profile that was obtained from the file or a list of Profiles.
def read(self, file_name):
- raise NotImplementedError("Profile reader plug-in was not correctly implemented. The read function was not implemented.")
\ No newline at end of file
+ raise NotImplementedError("Profile reader plug-in was not correctly implemented. The read function was not implemented.")
diff --git a/cura/QualityManager.py b/cura/QualityManager.py
deleted file mode 100644
index 76a0c86a5f..0000000000
--- a/cura/QualityManager.py
+++ /dev/null
@@ -1,332 +0,0 @@
-# Copyright (c) 2017 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-# This collects a lot of quality and quality changes related code which was split between ContainerManager
-# and the MachineManager and really needs to usable from both.
-from typing import Any, Dict, List, Optional, TYPE_CHECKING
-
-from UM.Application import Application
-from UM.Settings.ContainerRegistry import ContainerRegistry
-from UM.Settings.InstanceContainer import InstanceContainer
-from cura.Settings.ExtruderManager import ExtruderManager
-
-if TYPE_CHECKING:
- from cura.Settings.GlobalStack import GlobalStack
- from cura.Settings.ExtruderStack import ExtruderStack
- from UM.Settings.DefinitionContainer import DefinitionContainerInterface
-
-class QualityManager:
-
- ## Get the singleton instance for this class.
- @classmethod
- def getInstance(cls) -> "QualityManager":
- # Note: Explicit use of class name to prevent issues with inheritance.
- if not QualityManager.__instance:
- QualityManager.__instance = cls()
- return QualityManager.__instance
-
- __instance = None # type: "QualityManager"
-
- ## Find a quality by name for a specific machine definition and materials.
- #
- # \param quality_name
- # \param machine_definition (Optional) \type{DefinitionContainerInterface} If nothing is
- # specified then the currently selected machine definition is used.
- # \param material_containers_metadata If nothing is specified then the
- # current set of selected materials is used.
- # \return the matching quality container \type{InstanceContainer}
- def findQualityByName(self, quality_name: str, machine_definition: Optional["DefinitionContainerInterface"] = None, material_containers_metadata: Optional[List[Dict[str, Any]]] = None) -> Optional[InstanceContainer]:
- criteria = {"type": "quality", "name": quality_name}
- result = self._getFilteredContainersForStack(machine_definition, material_containers_metadata, **criteria)
-
- # Fall back to using generic materials and qualities if nothing could be found.
- if not result and material_containers_metadata and len(material_containers_metadata) == 1:
- basic_materials = self._getBasicMaterialMetadatas(material_containers_metadata[0])
- result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria)
-
- return result[0] if result else None
-
- ## Find a quality changes container by name.
- #
- # \param quality_changes_name \type{str} the name of the quality changes container.
- # \param machine_definition (Optional) \type{DefinitionContainer} If nothing is
- # specified then the currently selected machine definition is used..
- # \return the matching quality changes containers \type{List[InstanceContainer]}
- def findQualityChangesByName(self, quality_changes_name: str, machine_definition: Optional["DefinitionContainerInterface"] = None):
- if not machine_definition:
- global_stack = Application.getGlobalContainerStack()
- if not global_stack:
- return [] #No stack, so no current definition could be found, so there are no quality changes either.
- machine_definition = global_stack.definition
-
- result = self.findAllQualityChangesForMachine(machine_definition)
- result = [quality_change for quality_change in result if quality_change.getName() == quality_changes_name]
- return result
-
- ## Fetch the list of available quality types for this combination of machine definition and materials.
- #
- # \param machine_definition \type{DefinitionContainer}
- # \param material_containers \type{List[InstanceContainer]}
- # \return \type{List[str]}
- def findAllQualityTypesForMachineAndMaterials(self, machine_definition: "DefinitionContainerInterface", material_containers: List[InstanceContainer]) -> List[str]:
- # Determine the common set of quality types which can be
- # applied to all of the materials for this machine.
- quality_type_dict = self.__fetchQualityTypeDictForMaterial(machine_definition, material_containers[0])
- common_quality_types = set(quality_type_dict.keys())
- for material_container in material_containers[1:]:
- next_quality_type_dict = self.__fetchQualityTypeDictForMaterial(machine_definition, material_container)
- common_quality_types.intersection_update(set(next_quality_type_dict.keys()))
-
- return list(common_quality_types)
-
- def findAllQualitiesForMachineAndMaterials(self, machine_definition: "DefinitionContainerInterface", material_containers: List[InstanceContainer]) -> List[InstanceContainer]:
- # Determine the common set of quality types which can be
- # applied to all of the materials for this machine.
- quality_type_dict = self.__fetchQualityTypeDictForMaterial(machine_definition, material_containers[0])
- qualities = set(quality_type_dict.values())
- for material_container in material_containers[1:]:
- next_quality_type_dict = self.__fetchQualityTypeDictForMaterial(machine_definition, material_container)
- qualities.intersection_update(set(next_quality_type_dict.values()))
-
- return list(qualities)
-
- ## Fetches a dict of quality types names to quality profiles for a combination of machine and material.
- #
- # \param machine_definition \type{DefinitionContainer} the machine definition.
- # \param material \type{InstanceContainer} the material.
- # \return \type{Dict[str, InstanceContainer]} the dict of suitable quality type names mapping to qualities.
- def __fetchQualityTypeDictForMaterial(self, machine_definition: "DefinitionContainerInterface", material: InstanceContainer) -> Dict[str, InstanceContainer]:
- qualities = self.findAllQualitiesForMachineMaterial(machine_definition, material)
- quality_type_dict = {}
- for quality in qualities:
- quality_type_dict[quality.getMetaDataEntry("quality_type")] = quality
- return quality_type_dict
-
- ## Find a quality container by quality type.
- #
- # \param quality_type \type{str} the name of the quality type to search for.
- # \param machine_definition (Optional) \type{InstanceContainer} If nothing is
- # specified then the currently selected machine definition is used.
- # \param material_containers_metadata If nothing is specified then the
- # current set of selected materials is used.
- # \return the matching quality container \type{InstanceContainer}
- def findQualityByQualityType(self, quality_type: str, machine_definition: Optional["DefinitionContainerInterface"] = None, material_containers_metadata: Optional[List[Dict[str, Any]]] = None, **kwargs) -> InstanceContainer:
- criteria = kwargs
- criteria["type"] = "quality"
- if quality_type:
- criteria["quality_type"] = quality_type
- result = self._getFilteredContainersForStack(machine_definition, material_containers_metadata, **criteria)
- # Fall back to using generic materials and qualities if nothing could be found.
- if not result and material_containers_metadata and len(material_containers_metadata) == 1:
- basic_materials = self._getBasicMaterialMetadatas(material_containers_metadata[0])
- if basic_materials:
- result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria)
- return result[0] if result else None
-
- ## Find all suitable qualities for a combination of machine and material.
- #
- # \param machine_definition \type{DefinitionContainer} the machine definition.
- # \param material_container \type{InstanceContainer} the material.
- # \return \type{List[InstanceContainer]} the list of suitable qualities.
- def findAllQualitiesForMachineMaterial(self, machine_definition: "DefinitionContainerInterface", material_container: InstanceContainer) -> List[InstanceContainer]:
- criteria = {"type": "quality"}
- result = self._getFilteredContainersForStack(machine_definition, [material_container.getMetaData()], **criteria)
- if not result:
- basic_materials = self._getBasicMaterialMetadatas(material_container.getMetaData())
- if basic_materials:
- result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria)
-
- return result
-
- ## Find all quality changes for a machine.
- #
- # \param machine_definition \type{DefinitionContainer} the machine definition.
- # \return \type{List[InstanceContainer]} the list of quality changes
- def findAllQualityChangesForMachine(self, machine_definition: "DefinitionContainerInterface") -> List[InstanceContainer]:
- if machine_definition.getMetaDataEntry("has_machine_quality"):
- definition_id = machine_definition.getId()
- else:
- definition_id = "fdmprinter"
-
- filter_dict = { "type": "quality_changes", "definition": definition_id }
- quality_changes_list = ContainerRegistry.getInstance().findInstanceContainers(**filter_dict)
- return quality_changes_list
-
- def findAllExtruderDefinitionsForMachine(self, machine_definition: "DefinitionContainerInterface") -> List["DefinitionContainerInterface"]:
- filter_dict = { "machine": machine_definition.getId() }
- return ContainerRegistry.getInstance().findDefinitionContainers(**filter_dict)
-
- ## Find all quality changes for a given extruder.
- #
- # \param extruder_definition The extruder to find the quality changes for.
- # \return The list of quality changes for the given extruder.
- def findAllQualityChangesForExtruder(self, extruder_definition: "DefinitionContainerInterface") -> List[InstanceContainer]:
- filter_dict = {"type": "quality_changes", "extruder": extruder_definition.getId()}
- return ContainerRegistry.getInstance().findInstanceContainers(**filter_dict)
-
- ## Find all usable qualities for a machine and extruders.
- #
- # Finds all of the qualities for this combination of machine and extruders.
- # Only one quality per quality type is returned. i.e. if there are 2 qualities with quality_type=normal
- # then only one of then is returned (at random).
- #
- # \param global_container_stack \type{GlobalStack} the global machine definition
- # \param extruder_stacks \type{List[ExtruderStack]} the list of extruder stacks
- # \return \type{List[InstanceContainer]} the list of the matching qualities. The quality profiles
- # return come from the first extruder in the given list of extruders.
- def findAllUsableQualitiesForMachineAndExtruders(self, global_container_stack: "GlobalStack", extruder_stacks: List["ExtruderStack"]) -> List[InstanceContainer]:
- global_machine_definition = global_container_stack.getBottom()
-
- machine_manager = Application.getInstance().getMachineManager()
- active_stack_id = machine_manager.activeStackId
-
- materials = []
-
- for stack in extruder_stacks:
- if stack.getId() == active_stack_id and machine_manager.newMaterial:
- materials.append(machine_manager.newMaterial)
- else:
- materials.append(stack.material)
-
- quality_types = self.findAllQualityTypesForMachineAndMaterials(global_machine_definition, materials)
-
- # Map the list of quality_types to InstanceContainers
- qualities = self.findAllQualitiesForMachineMaterial(global_machine_definition, materials[0])
- quality_type_dict = {}
- for quality in qualities:
- quality_type_dict[quality.getMetaDataEntry("quality_type")] = quality
-
- return [quality_type_dict[quality_type] for quality_type in quality_types]
-
- ## Fetch more basic versions of a material.
- #
- # This tries to find a generic or basic version of the given material.
- # \param material_container \type{Dict[str, Any]} The metadata of a
- # material to find the basic versions of.
- # \return \type{List[Dict[str, Any]]} A list of the metadata of basic
- # materials, or an empty list if none could be found.
- def _getBasicMaterialMetadatas(self, material_container: Dict[str, Any]) -> List[Dict[str, Any]]:
- if "definition" not in material_container:
- definition_id = "fdmprinter"
- else:
- material_container_definition = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = material_container["definition"])
- if not material_container_definition:
- definition_id = "fdmprinter"
- else:
- material_container_definition = material_container_definition[0]
- if "has_machine_quality" not in material_container_definition:
- definition_id = "fdmprinter"
- else:
- definition_id = material_container_definition.get("quality_definition", material_container_definition["id"])
-
- base_material = material_container.get("material")
- if base_material:
- # There is a basic material specified
- criteria = {
- "type": "material",
- "name": base_material,
- "definition": definition_id,
- "variant": material_container.get("variant")
- }
- containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(**criteria)
- return containers
-
- return []
-
- def _getFilteredContainers(self, **kwargs):
- return self._getFilteredContainersForStack(None, None, **kwargs)
-
- def _getFilteredContainersForStack(self, machine_definition: "DefinitionContainerInterface" = None, material_metadata: Optional[List[Dict[str, Any]]] = None, **kwargs):
- # Fill in any default values.
- if machine_definition is None:
- machine_definition = Application.getInstance().getGlobalContainerStack().getBottom()
- quality_definition_id = machine_definition.getMetaDataEntry("quality_definition")
- if quality_definition_id is not None:
- machine_definition = ContainerRegistry.getInstance().findDefinitionContainers(id = quality_definition_id)[0]
-
- if not material_metadata:
- active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks()
- if active_stacks:
- material_metadata = [stack.material.getMetaData() for stack in active_stacks]
-
- criteria = kwargs
- filter_by_material = False
-
- machine_definition = self.getParentMachineDefinition(machine_definition)
- criteria["definition"] = machine_definition.getId()
- found_containers_with_machine_definition = ContainerRegistry.getInstance().findInstanceContainersMetadata(**criteria)
- whole_machine_definition = self.getWholeMachineDefinition(machine_definition)
- if whole_machine_definition.getMetaDataEntry("has_machine_quality"):
- definition_id = machine_definition.getMetaDataEntry("quality_definition", whole_machine_definition.getId())
- criteria["definition"] = definition_id
-
- filter_by_material = whole_machine_definition.getMetaDataEntry("has_materials")
- # only fall back to "fdmprinter" when there is no container for this machine
- elif not found_containers_with_machine_definition:
- criteria["definition"] = "fdmprinter"
-
- # Stick the material IDs in a set
- material_ids = set()
-
- for material_instance in material_metadata:
- if material_instance is not None:
- # Add the parent material too.
- for basic_material in self._getBasicMaterialMetadatas(material_instance):
- material_ids.add(basic_material["id"])
- material_ids.add(material_instance["id"])
- containers = ContainerRegistry.getInstance().findInstanceContainers(**criteria)
-
- result = []
- for container in containers:
- # If the machine specifies we should filter by material, exclude containers that do not match any active material.
- if filter_by_material and container.getMetaDataEntry("material") not in material_ids and "global_quality" not in kwargs:
- continue
- result.append(container)
-
- return result
-
- ## Get the parent machine definition of a machine definition.
- #
- # \param machine_definition \type{DefinitionContainer} This may be a normal machine definition or
- # an extruder definition.
- # \return \type{DefinitionContainer} the parent machine definition. If the given machine
- # definition doesn't have a parent then it is simply returned.
- def getParentMachineDefinition(self, machine_definition: "DefinitionContainerInterface") -> "DefinitionContainerInterface":
- container_registry = ContainerRegistry.getInstance()
-
- machine_entry = machine_definition.getMetaDataEntry("machine")
- if machine_entry is None:
- # We have a normal (whole) machine defintion
- quality_definition = machine_definition.getMetaDataEntry("quality_definition")
- if quality_definition is not None:
- parent_machine_definition = container_registry.findDefinitionContainers(id = quality_definition)[0]
- return self.getParentMachineDefinition(parent_machine_definition)
- else:
- return machine_definition
- else:
- # This looks like an extruder. Find the rest of the machine.
- whole_machine = container_registry.findDefinitionContainers(id = machine_entry)[0]
- parent_machine = self.getParentMachineDefinition(whole_machine)
- if whole_machine is parent_machine:
- # This extruder already belongs to a 'parent' machine def.
- return machine_definition
- else:
- # Look up the corresponding extruder definition in the parent machine definition.
- extruder_position = machine_definition.getMetaDataEntry("position")
- parent_extruder_id = parent_machine.getMetaDataEntry("machine_extruder_trains")[extruder_position]
- return container_registry.findDefinitionContainers(id = parent_extruder_id)[0]
-
- ## Get the whole/global machine definition from an extruder definition.
- #
- # \param machine_definition \type{DefinitionContainer} This may be a normal machine definition or
- # an extruder definition.
- # \return \type{DefinitionContainerInterface}
- def getWholeMachineDefinition(self, machine_definition: "DefinitionContainerInterface") -> "DefinitionContainerInterface":
- machine_entry = machine_definition.getMetaDataEntry("machine")
- if machine_entry is None:
- # This already is a 'global' machine definition.
- return machine_definition
- else:
- container_registry = ContainerRegistry.getInstance()
- whole_machine = container_registry.findDefinitionContainers(id = machine_entry)[0]
- return whole_machine
diff --git a/cura/Scene/BuildPlateDecorator.py b/cura/Scene/BuildPlateDecorator.py
index c2fd3145dd..dfb465b7ad 100644
--- a/cura/Scene/BuildPlateDecorator.py
+++ b/cura/Scene/BuildPlateDecorator.py
@@ -15,7 +15,7 @@ class BuildPlateDecorator(SceneNodeDecorator):
self._build_plate_number = nr
if isinstance(self._node, CuraSceneNode):
self._node.transformChanged() # trigger refresh node without introducing a new signal
- if self._node and self._node.callDecoration("isGroup"):
+ if self._node:
for child in self._node.getChildren():
child.callDecoration("setBuildPlateNumber", nr)
diff --git a/cura/Scene/ConvexHullDecorator.py b/cura/Scene/ConvexHullDecorator.py
index 3a563c2764..66bc8a7fc3 100644
--- a/cura/Scene/ConvexHullDecorator.py
+++ b/cura/Scene/ConvexHullDecorator.py
@@ -1,6 +1,8 @@
# Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
+from PyQt5.QtCore import QTimer
+
from UM.Application import Application
from UM.Math.Polygon import Polygon
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
@@ -22,6 +24,10 @@ class ConvexHullDecorator(SceneNodeDecorator):
self._global_stack = None
+ # Make sure the timer is created on the main thread
+ self._recompute_convex_hull_timer = None
+ Application.getInstance().callLater(self.createRecomputeConvexHullTimer)
+
self._raft_thickness = 0.0
# For raft thickness, DRY
self._build_volume = Application.getInstance().getBuildVolume()
@@ -33,6 +39,12 @@ class ConvexHullDecorator(SceneNodeDecorator):
self._onGlobalStackChanged()
+ def createRecomputeConvexHullTimer(self):
+ self._recompute_convex_hull_timer = QTimer()
+ self._recompute_convex_hull_timer.setInterval(200)
+ self._recompute_convex_hull_timer.setSingleShot(True)
+ self._recompute_convex_hull_timer.timeout.connect(self.recomputeConvexHull)
+
def setNode(self, node):
previous_node = self._node
# Disconnect from previous node signals
@@ -99,6 +111,12 @@ class ConvexHullDecorator(SceneNodeDecorator):
return self._compute2DConvexHull()
return None
+ def recomputeConvexHullDelayed(self):
+ if self._recompute_convex_hull_timer is not None:
+ self._recompute_convex_hull_timer.start()
+ else:
+ self.recomputeConvexHull()
+
def recomputeConvexHull(self):
controller = Application.getInstance().getController()
root = controller.getScene().getRoot()
@@ -279,7 +297,8 @@ class ConvexHullDecorator(SceneNodeDecorator):
def _onChanged(self, *args):
self._raft_thickness = self._build_volume.getRaftThickness()
- self.recomputeConvexHull()
+ if not args or args[0] == self._node:
+ self.recomputeConvexHullDelayed()
def _onGlobalStackChanged(self):
if self._global_stack:
diff --git a/cura/Scene/ConvexHullNode.py b/cura/Scene/ConvexHullNode.py
index 6c8c201498..1131958627 100644
--- a/cura/Scene/ConvexHullNode.py
+++ b/cura/Scene/ConvexHullNode.py
@@ -68,7 +68,7 @@ class ConvexHullNode(SceneNode):
ConvexHullNode.shader.setUniformValue("u_opacity", 0.6)
if self.getParent():
- if self.getMeshData() and isinstance(self._node, SceneNode) and self._node.callDecoration("getBuildPlateNumber") == Application.getInstance().getBuildPlateModel().activeBuildPlate:
+ if self.getMeshData() and isinstance(self._node, SceneNode) and self._node.callDecoration("getBuildPlateNumber") == Application.getInstance().getMultiBuildPlateModel().activeBuildPlate:
renderer.queueNode(self, transparent = True, shader = ConvexHullNode.shader, backface_cull = True, sort = -8)
if self._convex_hull_head_mesh:
renderer.queueNode(self, shader = ConvexHullNode.shader, transparent = True, mesh = self._convex_hull_head_mesh, backface_cull = True, sort = -8)
diff --git a/cura/Scene/CuraSceneController.py b/cura/Scene/CuraSceneController.py
index a93a8769d0..749c5257a2 100644
--- a/cura/Scene/CuraSceneController.py
+++ b/cura/Scene/CuraSceneController.py
@@ -4,7 +4,7 @@ from PyQt5.QtCore import Qt, pyqtSlot, QObject
from PyQt5.QtWidgets import QApplication
from cura.ObjectsModel import ObjectsModel
-from cura.BuildPlateModel import BuildPlateModel
+from cura.Machines.Models.MultiBuildPlateModel import MultiBuildPlateModel
from UM.Application import Application
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
@@ -16,11 +16,11 @@ from UM.Signal import Signal
class CuraSceneController(QObject):
activeBuildPlateChanged = Signal()
- def __init__(self, objects_model: ObjectsModel, build_plate_model: BuildPlateModel):
+ def __init__(self, objects_model: ObjectsModel, multi_build_plate_model: MultiBuildPlateModel):
super().__init__()
self._objects_model = objects_model
- self._build_plate_model = build_plate_model
+ self._multi_build_plate_model = multi_build_plate_model
self._active_build_plate = -1
self._last_selected_index = 0
@@ -41,9 +41,9 @@ class CuraSceneController(QObject):
self._max_build_plate = max_build_plate
changed = True
if changed:
- self._build_plate_model.setMaxBuildPlate(self._max_build_plate)
+ self._multi_build_plate_model.setMaxBuildPlate(self._max_build_plate)
build_plates = [{"name": "Build Plate %d" % (i + 1), "buildPlateNumber": i} for i in range(self._max_build_plate + 1)]
- self._build_plate_model.setItems(build_plates)
+ self._multi_build_plate_model.setItems(build_plates)
if self._active_build_plate > self._max_build_plate:
build_plate_number = 0
if self._last_selected_index >= 0: # go to the buildplate of the item you last selected
@@ -59,6 +59,8 @@ class CuraSceneController(QObject):
for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()):
if node.callDecoration("isSliceable"):
build_plate_number = node.callDecoration("getBuildPlateNumber")
+ if build_plate_number is None:
+ build_plate_number = 0
max_build_plate = max(build_plate_number, max_build_plate)
return max_build_plate
@@ -102,12 +104,12 @@ class CuraSceneController(QObject):
self._active_build_plate = nr
Selection.clear()
- self._build_plate_model.setActiveBuildPlate(nr)
+ self._multi_build_plate_model.setActiveBuildPlate(nr)
self._objects_model.setActiveBuildPlate(nr)
self.activeBuildPlateChanged.emit()
@staticmethod
def createCuraSceneController():
objects_model = Application.getInstance().getObjectsModel()
- build_plate_model = Application.getInstance().getBuildPlateModel()
- return CuraSceneController(objects_model = objects_model, build_plate_model = build_plate_model)
+ multi_build_plate_model = Application.getInstance().getMultiBuildPlateModel()
+ return CuraSceneController(objects_model = objects_model, multi_build_plate_model = multi_build_plate_model)
diff --git a/cura/Scene/CuraSceneNode.py b/cura/Scene/CuraSceneNode.py
index 969d491f49..48d271a2f2 100644
--- a/cura/Scene/CuraSceneNode.py
+++ b/cura/Scene/CuraSceneNode.py
@@ -1,9 +1,13 @@
+# 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
from UM.Application import Application
+from UM.Math.AxisAlignedBox import AxisAlignedBox
from UM.Scene.SceneNode import SceneNode
-from copy import deepcopy
-from cura.Settings.ExtrudersModel import ExtrudersModel
+
+from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
## Scene nodes that are models are only seen when selecting the corresponding build plate
@@ -11,6 +15,8 @@ from cura.Settings.ExtrudersModel import ExtrudersModel
class CuraSceneNode(SceneNode):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
+ if "no_setting_override" not in kwargs:
+ self.addDecorator(SettingOverrideDecorator()) # now we always have a getActiveExtruderPosition, unless explicitly disabled
self._outside_buildarea = False
def setOutsideBuildArea(self, new_value):
@@ -20,10 +26,10 @@ class CuraSceneNode(SceneNode):
return self._outside_buildarea or self.callDecoration("getBuildPlateNumber") < 0
def isVisible(self):
- return super().isVisible() and self.callDecoration("getBuildPlateNumber") == Application.getInstance().getBuildPlateModel().activeBuildPlate
+ return super().isVisible() and self.callDecoration("getBuildPlateNumber") == Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
def isSelectable(self) -> bool:
- return super().isSelectable() and self.callDecoration("getBuildPlateNumber") == Application.getInstance().getBuildPlateModel().activeBuildPlate
+ return super().isSelectable() and self.callDecoration("getBuildPlateNumber") == Application.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
@@ -35,7 +41,7 @@ class CuraSceneNode(SceneNode):
# Use the support extruder instead of the active extruder if this is a support_mesh
if per_mesh_stack:
if per_mesh_stack.getProperty("support_mesh", "value"):
- return extruders[int(global_container_stack.getProperty("support_extruder_nr", "value"))]
+ return extruders[int(global_container_stack.getExtruderPositionValueWithDefault("support_extruder_nr"))]
# It's only set if you explicitly choose an extruder
extruder_id = self.callDecoration("getActiveExtruder")
@@ -72,9 +78,34 @@ class CuraSceneNode(SceneNode):
1.0
]
+ ## Return if the provided bbox collides with the bbox of this scene node
+ def collidesWithBbox(self, check_bbox):
+ 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
+
+ return False
+
+ ## Return if any area collides with the convex hull of this scene node
+ def collidesWithArea(self, areas):
+ convex_hull = self.callDecoration("getConvexHull")
+ if convex_hull:
+ if not convex_hull.isValid():
+ return False
+
+ # Check for collisions between disallowed areas and the object
+ for area in areas:
+ overlap = convex_hull.intersectsPolygon(area)
+ if overlap is None:
+ continue
+ return True
+ return False
+
## Taken from SceneNode, but replaced SceneNode with CuraSceneNode
def __deepcopy__(self, memo):
- copy = 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))
diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py
index d2e8410dde..760a288b7b 100644
--- a/cura/Settings/ContainerManager.py
+++ b/cura/Settings/ContainerManager.py
@@ -1,16 +1,14 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
-import copy
import os.path
import urllib.parse
import uuid
-from typing import Any, Dict, List, Union
+from typing import Dict, Union
from PyQt5.QtCore import QObject, QUrl, QVariant
from UM.FlameProfiler import pyqtSlot
from PyQt5.QtWidgets import QMessageBox
-from UM.Util import parseBool
from UM.PluginRegistry import PluginRegistry
from UM.SaveFile import SaveFile
@@ -22,7 +20,6 @@ from UM.Application import Application
from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.DefinitionContainer import DefinitionContainer
from UM.Settings.InstanceContainer import InstanceContainer
-from cura.QualityManager import QualityManager
from UM.MimeTypeDatabase import MimeTypeNotFoundError
from UM.Settings.ContainerRegistry import ContainerRegistry
@@ -30,9 +27,11 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.i18n import i18nCatalog
from cura.Settings.ExtruderManager import ExtruderManager
+from cura.Settings.ExtruderStack import ExtruderStack
catalog = i18nCatalog("cura")
+
## Manager class that contains common actions to deal with containers in Cura.
#
# This is primarily intended as a class to be able to perform certain actions
@@ -42,154 +41,12 @@ class ContainerManager(QObject):
def __init__(self, parent = None):
super().__init__(parent)
+ self._application = Application.getInstance()
self._container_registry = ContainerRegistry.getInstance()
- self._machine_manager = Application.getInstance().getMachineManager()
+ self._machine_manager = self._application.getMachineManager()
+ self._material_manager = self._application.getMaterialManager()
self._container_name_filters = {}
- ## Create a duplicate of the specified container
- #
- # This will create and add a duplicate of the container corresponding
- # to the container ID.
- #
- # \param container_id \type{str} The ID of the container to duplicate.
- #
- # \return The ID of the new container, or an empty string if duplication failed.
- @pyqtSlot(str, result = str)
- def duplicateContainer(self, container_id):
- #TODO: It should be able to duplicate a container of which only the metadata is known.
- containers = self._container_registry.findContainers(id = container_id)
- if not containers:
- Logger.log("w", "Could duplicate container %s because it was not found.", container_id)
- return ""
-
- container = containers[0]
- new_container = self.duplicateContainerInstance(container)
- return new_container.getId()
-
- ## Create a duplicate of the given container instance
- #
- # This will create and add a duplicate of the container that was passed.
- #
- # \param container \type{ContainerInterface} The container to duplicate.
- #
- # \return The duplicated container, or None if duplication failed.
- def duplicateContainerInstance(self, container):
- new_container = None
- new_name = self._container_registry.uniqueName(container.getName())
- # Only InstanceContainer has a duplicate method at the moment.
- # So fall back to serialize/deserialize when no duplicate method exists.
- if hasattr(container, "duplicate"):
- new_container = container.duplicate(new_name)
- else:
- new_container = container.__class__(new_name)
- new_container.deserialize(container.serialize())
- new_container.setName(new_name)
-
- # TODO: we probably don't want to add it to the registry here!
- if new_container:
- self._container_registry.addContainer(new_container)
-
- return new_container
-
- ## Change the name of a specified container to a new name.
- #
- # \param container_id \type{str} The ID of the container to change the name of.
- # \param new_id \type{str} The new ID of the container.
- # \param new_name \type{str} The new name of the specified container.
- #
- # \return True if successful, False if not.
- @pyqtSlot(str, str, str, result = bool)
- def renameContainer(self, container_id, new_id, new_name):
- containers = self._container_registry.findContainers(id = container_id)
- if not containers:
- Logger.log("w", "Could rename container %s because it was not found.", container_id)
- return False
-
- container = containers[0]
- # First, remove the container from the registry. This will clean up any files related to the container.
- self._container_registry.removeContainer(container_id)
-
- # Ensure we have a unique name for the container
- new_name = self._container_registry.uniqueName(new_name)
-
- # Then, update the name and ID of the container
- container.setName(new_name)
- container._id = new_id # TODO: Find a nicer way to set a new, unique ID
-
- # Finally, re-add the container so it will be properly serialized again.
- self._container_registry.addContainer(container)
-
- return True
-
- ## Remove the specified container.
- #
- # \param container_id \type{str} The ID of the container to remove.
- #
- # \return True if the container was successfully removed, False if not.
- @pyqtSlot(str, result = bool)
- def removeContainer(self, container_id):
- containers = self._container_registry.findContainers(id = container_id)
- if not containers:
- Logger.log("w", "Could not remove container %s because it was not found.", container_id)
- return False
-
- self._container_registry.removeContainer(containers[0].getId())
-
- return True
-
- ## Merge a container with another.
- #
- # This will try to merge one container into the other, by going through the container
- # and setting the right properties on the other container.
- #
- # \param merge_into_id \type{str} The ID of the container to merge into.
- # \param merge_id \type{str} The ID of the container to merge.
- #
- # \return True if successfully merged, False if not.
- @pyqtSlot(str, result = bool)
- def mergeContainers(self, merge_into_id, merge_id):
- containers = self._container_registry.findContainers(id = merge_into_id)
- if not containers:
- Logger.log("w", "Could merge into container %s because it was not found.", merge_into_id)
- return False
-
- merge_into = containers[0]
-
- containers = self._container_registry.findContainers(id = merge_id)
- if not containers:
- Logger.log("w", "Could not merge container %s because it was not found", merge_id)
- return False
-
- merge = containers[0]
-
- if not isinstance(merge, type(merge_into)):
- Logger.log("w", "Cannot merge two containers of different types")
- return False
-
- self._performMerge(merge_into, merge)
-
- return True
-
- ## Clear the contents of a container.
- #
- # \param container_id \type{str} The ID of the container to clear.
- #
- # \return True if successful, False if not.
- @pyqtSlot(str, result = bool)
- def clearContainer(self, container_id):
- if self._container_registry.isReadOnly(container_id):
- Logger.log("w", "Cannot clear read-only container %s", container_id)
- return False
-
- containers = self._container_registry.findContainers(id = container_id)
- if not containers:
- Logger.log("w", "Could clear container %s because it was not found.", container_id)
- return False
-
- containers[0].clear()
-
- return True
-
@pyqtSlot(str, str, result=str)
def getContainerMetaDataEntry(self, container_id, entry_name):
metadatas = self._container_registry.findContainersMetadata(id = container_id)
@@ -211,18 +68,15 @@ class ContainerManager(QObject):
# \param entry_value The new value of the entry.
#
# \return True if successful, False if not.
- @pyqtSlot(str, str, str, result = bool)
- def setContainerMetaDataEntry(self, container_id, entry_name, entry_value):
- if self._container_registry.isReadOnly(container_id):
- Logger.log("w", "Cannot set metadata of read-only container %s.", container_id)
+ # TODO: This is ONLY used by MaterialView for material containers. Maybe refactor this.
+ @pyqtSlot("QVariant", str, str)
+ def setContainerMetaDataEntry(self, container_node, entry_name, entry_value):
+ root_material_id = container_node.metadata["base_file"]
+ if self._container_registry.isReadOnly(root_material_id):
+ Logger.log("w", "Cannot set metadata of read-only container %s.", root_material_id)
return False
- containers = self._container_registry.findContainers(id = container_id) #We need the complete container, since we need to know whether the container is read-only or not.
- if not containers:
- Logger.log("w", "Could not set metadata of container %s because it was not found.", container_id)
- return False
-
- container = containers[0]
+ material_group = self._material_manager.getMaterialGroup(root_material_id)
entries = entry_name.split("/")
entry_name = entries.pop()
@@ -230,7 +84,7 @@ class ContainerManager(QObject):
sub_item_changed = False
if entries:
root_name = entries.pop(0)
- root = container.getMetaDataEntry(root_name)
+ root = material_group.root_material_node.metadata.get(root_name)
item = root
for _ in range(len(entries)):
@@ -243,12 +97,11 @@ class ContainerManager(QObject):
entry_name = root_name
entry_value = root
+ container = material_group.root_material_node.getContainer()
container.setMetaDataEntry(entry_name, entry_value)
if sub_item_changed: #If it was only a sub-item that has changed then the setMetaDataEntry won't correctly notice that something changed, and we must manually signal that the metadata changed.
container.metaDataChanged.emit(container)
- return True
-
## Set a setting property of the specified container.
#
# This will set the specified property of the specified setting of the container
@@ -306,60 +159,6 @@ class ContainerManager(QObject):
return container.getProperty(setting_key, property_name)
- ## Set the name of the specified container.
- @pyqtSlot(str, str, result = bool)
- def setContainerName(self, container_id, new_name):
- if self._container_registry.isReadOnly(container_id):
- Logger.log("w", "Cannot set name of read-only container %s.", container_id)
- return False
-
- containers = self._container_registry.findContainers(id = container_id) #We need to get the full container, not just metadata, since we need to know whether it's read-only.
- if not containers:
- Logger.log("w", "Could not set name of container %s because it was not found.", container_id)
- return False
-
- containers[0].setName(new_name)
-
- return True
-
- ## Find instance containers matching certain criteria.
- #
- # This effectively forwards to
- # ContainerRegistry::findInstanceContainersMetadata.
- #
- # \param criteria A dict of key - value pairs to search for.
- #
- # \return A list of container IDs that match the given criteria.
- @pyqtSlot("QVariantMap", result = "QVariantList")
- def findInstanceContainers(self, criteria):
- return [entry["id"] for entry in self._container_registry.findInstanceContainersMetadata(**criteria)]
-
- @pyqtSlot(str, result = bool)
- def isContainerUsed(self, container_id):
- Logger.log("d", "Checking if container %s is currently used", container_id)
- # check if this is a material container. If so, check if any material with the same base is being used by any
- # stacks.
- container_ids_to_check = [container_id]
- container_results = self._container_registry.findInstanceContainersMetadata(id = container_id, type = "material")
- if container_results:
- this_container = container_results[0]
- material_base_file = this_container["id"]
- if "base_file" in this_container:
- material_base_file = this_container["base_file"]
- # check all material container IDs with the same base
- material_containers = self._container_registry.findInstanceContainersMetadata(base_file = material_base_file,
- type = "material")
- if material_containers:
- container_ids_to_check = [container["id"] for container in material_containers]
-
- all_stacks = self._container_registry.findContainerStacks()
- for stack in all_stacks:
- for used_container_id in container_ids_to_check:
- if used_container_id in [child.getId() for child in stack.getContainers()]:
- Logger.log("d", "The container is in use by %s", stack.getId())
- return True
- return False
-
@pyqtSlot(str, result = str)
def makeUniqueName(self, original_name):
return self._container_registry.uniqueName(original_name)
@@ -399,7 +198,7 @@ class ContainerManager(QObject):
@pyqtSlot(str, str, QUrl, result = "QVariantMap")
def exportContainer(self, container_id: str, file_type: str, file_url_or_string: Union[QUrl, str]) -> Dict[str, str]:
if not container_id or not file_type or not file_url_or_string:
- return { "status": "error", "message": "Invalid arguments"}
+ return {"status": "error", "message": "Invalid arguments"}
if isinstance(file_url_or_string, QUrl):
file_url = file_url_or_string.toLocalFile()
@@ -407,20 +206,20 @@ class ContainerManager(QObject):
file_url = file_url_or_string
if not file_url:
- return { "status": "error", "message": "Invalid path"}
+ return {"status": "error", "message": "Invalid path"}
mime_type = None
- if not file_type in self._container_name_filters:
+ if file_type not in self._container_name_filters:
try:
mime_type = MimeTypeDatabase.getMimeTypeForFile(file_url)
except MimeTypeNotFoundError:
- return { "status": "error", "message": "Unknown File Type" }
+ return {"status": "error", "message": "Unknown File Type"}
else:
mime_type = self._container_name_filters[file_type]["mime"]
containers = self._container_registry.findContainers(id = container_id)
if not containers:
- return { "status": "error", "message": "Container not found"}
+ return {"status": "error", "message": "Container not found"}
container = containers[0]
if Platform.isOSX() and "." in file_url:
@@ -437,12 +236,12 @@ class ContainerManager(QObject):
result = QMessageBox.question(None, catalog.i18nc("@title:window", "File Already Exists"),
catalog.i18nc("@label Don't translate the XML tag !", "The file {0} already exists. Are you sure you want to overwrite it?").format(file_url))
if result == QMessageBox.No:
- return { "status": "cancelled", "message": "User cancelled"}
+ return {"status": "cancelled", "message": "User cancelled"}
try:
contents = container.serialize()
except NotImplementedError:
- return { "status": "error", "message": "Unable to serialize container"}
+ return {"status": "error", "message": "Unable to serialize container"}
if contents is None:
return {"status": "error", "message": "Serialization returned None. Unable to write to file"}
@@ -450,7 +249,7 @@ class ContainerManager(QObject):
with SaveFile(file_url, "w") as f:
f.write(contents)
- return { "status": "success", "message": "Succesfully exported container", "path": file_url}
+ return {"status": "success", "message": "Successfully exported container", "path": file_url}
## Imports a profile from a file
#
@@ -461,7 +260,7 @@ class ContainerManager(QObject):
@pyqtSlot(QUrl, result = "QVariantMap")
def importMaterialContainer(self, file_url_or_string: Union[QUrl, str]) -> Dict[str, str]:
if not file_url_or_string:
- return { "status": "error", "message": "Invalid path"}
+ return {"status": "error", "message": "Invalid path"}
if isinstance(file_url_or_string, QUrl):
file_url = file_url_or_string.toLocalFile()
@@ -469,16 +268,16 @@ class ContainerManager(QObject):
file_url = file_url_or_string
if not file_url or not os.path.exists(file_url):
- return { "status": "error", "message": "Invalid path" }
+ return {"status": "error", "message": "Invalid path"}
try:
mime_type = MimeTypeDatabase.getMimeTypeForFile(file_url)
except MimeTypeNotFoundError:
- return { "status": "error", "message": "Could not determine mime type of file" }
+ return {"status": "error", "message": "Could not determine mime type of file"}
container_type = self._container_registry.getContainerForMimeType(mime_type)
if not container_type:
- return { "status": "error", "message": "Could not find a container to handle the specified file."}
+ return {"status": "error", "message": "Could not find a container to handle the specified file."}
container_id = urllib.parse.unquote_plus(mime_type.stripExtension(os.path.basename(file_url)))
container_id = self._container_registry.uniqueName(container_id)
@@ -489,7 +288,7 @@ class ContainerManager(QObject):
with open(file_url, "rt", encoding = "utf-8") as f:
container.deserialize(f.read())
except PermissionError:
- return { "status": "error", "message": "Permission denied when trying to read the file"}
+ return {"status": "error", "message": "Permission denied when trying to read the file"}
except Exception as ex:
return {"status": "error", "message": str(ex)}
@@ -497,7 +296,7 @@ class ContainerManager(QObject):
self._container_registry.addContainer(container)
- return { "status": "success", "message": "Successfully imported container {0}".format(container.getName()) }
+ return {"status": "success", "message": "Successfully imported container {0}".format(container.getName())}
## Update the current active quality changes container with the settings from the user container.
#
@@ -522,7 +321,7 @@ class ContainerManager(QObject):
self._performMerge(quality_changes, stack.getTop())
- self._machine_manager.activeQualityChanged.emit()
+ self._machine_manager.activeQualityChangesGroupChanged.emit()
return True
@@ -535,414 +334,50 @@ class ContainerManager(QObject):
# Go through global and extruder stacks and clear their topmost container (the user settings).
for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
- container = stack.getTop()
+ container = stack.userChanges
container.clear()
send_emits_containers.append(container)
+ # user changes are possibly added to make the current setup match the current enabled extruders
+ Application.getInstance().getMachineManager().correctExtruderSettings()
+
for container in send_emits_containers:
container.sendPostponedEmits()
- ## Create quality changes containers from the user containers in the active stacks.
- #
- # This will go through the global and extruder stacks and create quality_changes containers from
- # the user containers in each stack. These then replace the quality_changes containers in the
- # stack and clear the user settings.
- #
- # \return \type{bool} True if the operation was successfully, False if not.
- @pyqtSlot(str, result = bool)
- def createQualityChanges(self, base_name):
- global_stack = Application.getInstance().getGlobalContainerStack()
- if not global_stack:
- return False
-
- active_quality_name = self._machine_manager.activeQualityName
- if active_quality_name == "":
- Logger.log("w", "No quality container found in stack %s, cannot create profile", global_stack.getId())
- return False
-
- self._machine_manager.blurSettings.emit()
- if base_name is None or base_name == "":
- base_name = active_quality_name
- unique_name = self._container_registry.uniqueName(base_name)
-
- # Go through the active stacks and create quality_changes containers from the user containers.
- for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
- user_container = stack.getTop()
- quality_container = stack.quality
- quality_changes_container = stack.qualityChanges
- if not quality_container or not quality_changes_container:
- Logger.log("w", "No quality or quality changes container found in stack %s, ignoring it", stack.getId())
- continue
-
- extruder_id = None if stack is global_stack else QualityManager.getInstance().getParentMachineDefinition(stack.getBottom()).getId()
- new_changes = self._createQualityChanges(quality_container, unique_name,
- Application.getInstance().getGlobalContainerStack().getBottom(),
- extruder_id)
- self._performMerge(new_changes, quality_changes_container, clear_settings = False)
- self._performMerge(new_changes, user_container)
-
- self._container_registry.addContainer(new_changes)
- stack.replaceContainer(stack.getContainerIndex(quality_changes_container), new_changes)
-
- self._machine_manager.activeQualityChanged.emit()
- return True
-
- ## Remove all quality changes containers matching a specified name.
- #
- # This will search for quality_changes containers matching the supplied name and remove them.
- # Note that if the machine specifies that qualities should be filtered by machine and/or material
- # only the containers related to the active machine/material are removed.
- #
- # \param quality_name The name of the quality changes to remove.
- #
- # \return \type{bool} True if successful, False if not.
- @pyqtSlot(str, result = bool)
- def removeQualityChanges(self, quality_name):
- Logger.log("d", "Attempting to remove the quality change containers with name %s", quality_name)
- containers_found = False
-
- if not quality_name:
- return containers_found # Without a name we will never find a container to remove.
-
- # If the container that is being removed is the currently active quality, set another quality as the active quality
- activate_quality = quality_name == self._machine_manager.activeQualityName
- activate_quality_type = None
-
- global_stack = Application.getInstance().getGlobalContainerStack()
- if not global_stack or not quality_name:
- return ""
- machine_definition = QualityManager.getInstance().getParentMachineDefinition(global_stack.getBottom())
-
- for container in QualityManager.getInstance().findQualityChangesByName(quality_name, machine_definition):
- containers_found = True
- if activate_quality and not activate_quality_type:
- activate_quality_type = container.getMetaDataEntry("quality")
- self._container_registry.removeContainer(container.getId())
-
- if not containers_found:
- Logger.log("d", "Unable to remove quality containers, as we did not find any by the name of %s", quality_name)
-
- elif activate_quality:
- definition_id = "fdmprinter" if not self._machine_manager.filterQualityByMachine else self._machine_manager.activeDefinitionId
- containers = self._container_registry.findInstanceContainersMetadata(type = "quality", definition = definition_id, quality_type = activate_quality_type)
- if containers:
- self._machine_manager.setActiveQuality(containers[0]["id"])
- self._machine_manager.activeQualityChanged.emit()
-
- return containers_found
-
- ## Rename a set of quality changes containers.
- #
- # This will search for quality_changes containers matching the supplied name and rename them.
- # Note that if the machine specifies that qualities should be filtered by machine and/or material
- # only the containers related to the active machine/material are renamed.
- #
- # \param quality_name The name of the quality changes containers to rename.
- # \param new_name The new name of the quality changes.
- #
- # \return True if successful, False if not.
- @pyqtSlot(str, str, result = bool)
- def renameQualityChanges(self, quality_name, new_name):
- Logger.log("d", "User requested QualityChanges container rename of %s to %s", quality_name, new_name)
- if not quality_name or not new_name:
- return False
-
- if quality_name == new_name:
- Logger.log("w", "Unable to rename %s to %s, because they are the same.", quality_name, new_name)
- return True
-
- global_stack = Application.getInstance().getGlobalContainerStack()
- if not global_stack:
- return False
-
- self._machine_manager.blurSettings.emit()
-
- new_name = self._container_registry.uniqueName(new_name)
-
- container_registry = self._container_registry
-
- containers_to_rename = self._container_registry.findInstanceContainersMetadata(type = "quality_changes", name = quality_name)
-
- for container in containers_to_rename:
- stack_id = global_stack.getId()
- if "extruder" in container:
- stack_id = container["extruder"]
- container_registry.renameContainer(container["id"], new_name, self._createUniqueId(stack_id, new_name))
-
- if not containers_to_rename:
- Logger.log("e", "Unable to rename %s, because we could not find the profile", quality_name)
-
- self._machine_manager.activeQualityChanged.emit()
- return True
-
- ## Duplicate a specified set of quality or quality_changes containers.
- #
- # This will search for containers matching the specified name. If the container is a "quality" type container, a new
- # quality_changes container will be created with the specified quality as base. If the container is a "quality_changes"
- # container, it is simply duplicated and renamed.
- #
- # \param quality_name The name of the quality to duplicate.
- #
- # \return A string containing the name of the duplicated containers, or an empty string if it failed.
- @pyqtSlot(str, str, result = str)
- def duplicateQualityOrQualityChanges(self, quality_name, base_name):
- global_stack = Application.getInstance().getGlobalContainerStack()
- if not global_stack or not quality_name:
- return ""
- machine_definition = global_stack.definition
-
- active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks()
- if active_stacks is None:
- return ""
- material_metadatas = [stack.material.getMetaData() for stack in active_stacks]
-
- result = self._duplicateQualityOrQualityChangesForMachineType(quality_name, base_name,
- QualityManager.getInstance().getParentMachineDefinition(machine_definition),
- material_metadatas)
- return result[0].getName() if result else ""
-
- ## Duplicate a quality or quality changes profile specific to a machine type
- #
- # \param quality_name The name of the quality or quality changes container to duplicate.
- # \param base_name The desired name for the new container.
- # \param machine_definition The machine with the specific machine type.
- # \param material_metadatas Metadata of materials
- # \return List of duplicated quality profiles.
- def _duplicateQualityOrQualityChangesForMachineType(self, quality_name: str, base_name: str, machine_definition: DefinitionContainer, material_metadatas: List[Dict[str, Any]]) -> List[InstanceContainer]:
- Logger.log("d", "Attempting to duplicate the quality %s", quality_name)
-
- if base_name is None:
- base_name = quality_name
- # Try to find a Quality with the name.
- container = QualityManager.getInstance().findQualityByName(quality_name, machine_definition, material_metadatas)
- if container:
- Logger.log("d", "We found a quality to duplicate.")
- return self._duplicateQualityForMachineType(container, base_name, machine_definition)
- Logger.log("d", "We found a quality_changes to duplicate.")
- # Assume it is a quality changes.
- return self._duplicateQualityChangesForMachineType(quality_name, base_name, machine_definition)
-
- # Duplicate a quality profile
- def _duplicateQualityForMachineType(self, quality_container, base_name, machine_definition) -> List[InstanceContainer]:
- if base_name is None:
- base_name = quality_container.getName()
- new_name = self._container_registry.uniqueName(base_name)
-
- new_change_instances = []
-
- # Handle the global stack first.
- global_changes = self._createQualityChanges(quality_container, new_name, machine_definition, None)
- new_change_instances.append(global_changes)
- self._container_registry.addContainer(global_changes)
-
- # Handle the extruders if present.
- extruders = machine_definition.getMetaDataEntry("machine_extruder_trains")
- if extruders:
- for extruder_id in extruders:
- extruder = extruders[extruder_id]
- new_changes = self._createQualityChanges(quality_container, new_name, machine_definition, extruder)
- new_change_instances.append(new_changes)
- self._container_registry.addContainer(new_changes)
-
- return new_change_instances
-
- # Duplicate a quality changes container
- def _duplicateQualityChangesForMachineType(self, quality_changes_name, base_name, machine_definition) -> List[InstanceContainer]:
- new_change_instances = []
- for container in QualityManager.getInstance().findQualityChangesByName(quality_changes_name,
- machine_definition):
- base_id = container.getMetaDataEntry("extruder")
- if not base_id:
- base_id = container.getDefinition().getId()
- new_unique_id = self._createUniqueId(base_id, base_name)
- new_container = container.duplicate(new_unique_id, base_name)
- new_change_instances.append(new_container)
- self._container_registry.addContainer(new_container)
-
- return new_change_instances
-
- ## Create a duplicate of a material, which has the same GUID and base_file metadata
- #
- # \return \type{str} the id of the newly created container.
- @pyqtSlot(str, result = str)
- def duplicateMaterial(self, material_id: str) -> str:
- original = self._container_registry.findContainersMetadata(id = material_id)
- if not original:
- Logger.log("d", "Unable to duplicate the material with id %s, because it doesn't exist.", material_id)
- return ""
- original = original[0]
-
- base_container_id = original.get("base_file")
- base_container = self._container_registry.findContainers(id = base_container_id)
- if not base_container:
- Logger.log("d", "Unable to duplicate the material with id {material_id}, because base_file {base_container_id} doesn't exist.".format(material_id = material_id, base_container_id = base_container_id))
- return ""
- base_container = base_container[0]
-
- #We'll copy all containers with the same base.
- #This way the correct variant and machine still gets assigned when loading the copy of the material.
- containers_to_copy = self._container_registry.findInstanceContainers(base_file = base_container_id)
-
- # Ensure all settings are saved.
- Application.getInstance().saveSettings()
-
- # Create a new ID & container to hold the data.
- new_containers = []
- new_base_id = self._container_registry.uniqueName(base_container.getId())
- new_base_container = copy.deepcopy(base_container)
- new_base_container.getMetaData()["id"] = new_base_id
- new_base_container.getMetaData()["base_file"] = new_base_id
- new_containers.append(new_base_container)
-
- #Clone all of them.
- clone_of_original = None #Keeping track of which one is the clone of the original material, since we need to return that.
- for container_to_copy in containers_to_copy:
- #Create unique IDs for every clone.
- current_id = container_to_copy.getId()
- new_id = new_base_id
- if container_to_copy.getMetaDataEntry("definition") != "fdmprinter":
- new_id += "_" + container_to_copy.getMetaDataEntry("definition")
- if container_to_copy.getMetaDataEntry("variant"):
- variant = self._container_registry.findContainers(id = container_to_copy.getMetaDataEntry("variant"))[0]
- new_id += "_" + variant.getName().replace(" ", "_")
- if current_id == material_id:
- clone_of_original = new_id
-
- new_container = copy.deepcopy(container_to_copy)
- new_container.getMetaData()["id"] = new_id
- new_container.getMetaData()["base_file"] = new_base_id
- new_containers.append(new_container)
-
- for container_to_add in new_containers:
- container_to_add.setDirty(True)
- ContainerRegistry.getInstance().addContainer(container_to_add)
- return self._getMaterialContainerIdForActiveMachine(clone_of_original)
-
- ## Create a duplicate of a material or it's original entry
- #
- # \return \type{str} the id of the newly created container.
- @pyqtSlot(str, result = str)
- def duplicateOriginalMaterial(self, material_id):
-
- # check if the given material has a base file (i.e. was shipped by default)
- base_file = self.getContainerMetaDataEntry(material_id, "base_file")
-
- if base_file == "":
- # there is no base file, so duplicate by ID
- return self.duplicateMaterial(material_id)
- else:
- # there is a base file, so duplicate the original material
- return self.duplicateMaterial(base_file)
-
- ## Create a new material by cloning Generic PLA for the current material diameter and setting the GUID to something unqiue
- #
- # \return \type{str} the id of the newly created container.
- @pyqtSlot(result = str)
- def createMaterial(self) -> str:
- # Ensure all settings are saved.
- Application.getInstance().saveSettings()
-
- global_stack = Application.getInstance().getGlobalContainerStack()
- if not global_stack:
- return ""
-
- approximate_diameter = str(round(global_stack.getProperty("material_diameter", "value")))
- containers = self._container_registry.findInstanceContainersMetadata(id = "generic_pla*", approximate_diameter = approximate_diameter)
- if not containers:
- Logger.log("d", "Unable to create a new material by cloning Generic PLA, because it cannot be found for the material diameter for this machine.")
- return ""
-
- base_file = containers[0].get("base_file")
- containers = self._container_registry.findInstanceContainers(id = base_file)
- if not containers:
- Logger.log("d", "Unable to create a new material by cloning Generic PLA, because the base file for Generic PLA for this machine can not be found.")
- return ""
-
- # Create a new ID & container to hold the data.
- new_id = self._container_registry.uniqueName("custom_material")
- container_type = type(containers[0]) # Always XMLMaterialProfile, since we specifically clone the base_file
- duplicated_container = container_type(new_id)
-
- # Instead of duplicating we load the data from the basefile again.
- # This ensures that the inheritance goes well and all "cut up" subclasses of the xmlMaterial profile
- # are also correctly created.
- with open(containers[0].getPath(), encoding="utf-8") as f:
- duplicated_container.deserialize(f.read())
-
- duplicated_container.setMetaDataEntry("GUID", str(uuid.uuid4()))
- duplicated_container.setMetaDataEntry("brand", catalog.i18nc("@label", "Custom"))
- # We're defaulting to PLA, as machines with material profiles don't like material types they don't know.
- # TODO: This is a hack, the only reason this is in now is to bandaid the problem as we're close to a release!
- duplicated_container.setMetaDataEntry("material", "PLA")
- duplicated_container.setName(catalog.i18nc("@label", "Custom Material"))
-
- self._container_registry.addContainer(duplicated_container)
- return self._getMaterialContainerIdForActiveMachine(new_id)
-
- ## Find the id of a material container based on the new material
- # Utilty function that is shared between duplicateMaterial and createMaterial
- #
- # \param base_file \type{str} the id of the created container.
- def _getMaterialContainerIdForActiveMachine(self, base_file):
- global_stack = Application.getInstance().getGlobalContainerStack()
- if not global_stack:
- return base_file
-
- has_machine_materials = parseBool(global_stack.getMetaDataEntry("has_machine_materials", default = False))
- has_variant_materials = parseBool(global_stack.getMetaDataEntry("has_variant_materials", default = False))
- has_variants = parseBool(global_stack.getMetaDataEntry("has_variants", default = False))
- if has_machine_materials or has_variant_materials:
- if has_variants:
- materials = self._container_registry.findInstanceContainersMetadata(type = "material", base_file = base_file, definition = global_stack.getBottom().getId(), variant = self._machine_manager.activeVariantId)
- else:
- materials = self._container_registry.findInstanceContainersMetadata(type = "material", base_file = base_file, definition = global_stack.getBottom().getId())
-
- if materials:
- return materials[0]["id"]
-
- Logger.log("w", "Unable to find a suitable container based on %s for the current machine.", base_file)
- return "" # do not activate a new material if a container can not be found
-
- return base_file
-
## Get a list of materials that have the same GUID as the reference material
#
# \param material_id \type{str} the id of the material for which to get the linked materials.
# \return \type{list} a list of names of materials with the same GUID
- @pyqtSlot(str, result = "QStringList")
- def getLinkedMaterials(self, material_id: str):
- containers = self._container_registry.findInstanceContainersMetadata(id = material_id)
- if not containers:
- Logger.log("d", "Unable to find materials linked to material with id %s, because it doesn't exist.", material_id)
- return []
+ @pyqtSlot("QVariant", bool, result = "QStringList")
+ def getLinkedMaterials(self, material_node, exclude_self = False):
+ guid = material_node.metadata["GUID"]
- material_container = containers[0]
- material_base_file = material_container.get("base_file", "")
- material_guid = material_container.get("GUID", "")
- if not material_guid:
- Logger.log("d", "Unable to find materials linked to material with id %s, because it doesn't have a GUID.", material_id)
- return []
+ self_root_material_id = material_node.metadata["base_file"]
+ material_group_list = self._material_manager.getMaterialGroupListByGUID(guid)
- containers = self._container_registry.findInstanceContainersMetadata(type = "material", GUID = material_guid)
linked_material_names = []
- for container in containers:
- if container["id"] in [material_id, material_base_file] or container.get("base_file") != container["id"]:
- continue
-
- linked_material_names.append(container["name"])
+ if material_group_list:
+ for material_group in material_group_list:
+ if exclude_self and material_group.name == self_root_material_id:
+ continue
+ linked_material_names.append(material_group.root_material_node.metadata["name"])
return linked_material_names
## Unlink a material from all other materials by creating a new GUID
# \param material_id \type{str} the id of the material to create a new GUID for.
- @pyqtSlot(str)
- def unlinkMaterial(self, material_id: str):
- containers = self._container_registry.findInstanceContainers(id=material_id)
- if not containers:
- Logger.log("d", "Unable to make the material with id %s unique, because it doesn't exist.", material_id)
- return ""
+ @pyqtSlot("QVariant")
+ def unlinkMaterial(self, material_node):
+ # Get the material group
+ material_group = self._material_manager.getMaterialGroup(material_node.metadata["base_file"])
- containers[0].setMetaDataEntry("GUID", str(uuid.uuid4()))
+ # Generate a new GUID
+ new_guid = str(uuid.uuid4())
+ # Update the GUID
+ # NOTE: We only need to set the root material container because XmlMaterialProfile.setMetaDataEntry() will
+ # take care of the derived containers too
+ container = material_group.root_material_node.getContainer()
+ container.setMetaDataEntry("GUID", new_guid)
## Get the singleton instance for this class.
@classmethod
@@ -960,8 +395,6 @@ class ContainerManager(QObject):
return ContainerManager.getInstance()
def _performMerge(self, merge_into, merge, clear_settings = True):
- assert isinstance(merge, type(merge_into))
-
if merge == merge_into:
return
@@ -1015,81 +448,6 @@ class ContainerManager(QObject):
name_filter = "{0} ({1})".format(mime_type.comment, suffix_list)
self._container_name_filters[name_filter] = entry
- ## Creates a unique ID for a container by prefixing the name with the stack ID.
- #
- # This method creates a unique ID for a container by prefixing it with a specified stack ID.
- # This is done to ensure we have an easily identified ID for quality changes, which have the
- # same name across several stacks.
- #
- # \param stack_id The ID of the stack to prepend.
- # \param container_name The name of the container that we are creating a unique ID for.
- #
- # \return Container name prefixed with stack ID, in lower case with spaces replaced by underscores.
- def _createUniqueId(self, stack_id, container_name):
- result = stack_id + "_" + container_name
- result = result.lower()
- result.replace(" ", "_")
- return result
-
- ## Create a quality changes container for a specified quality container.
- #
- # \param quality_container The quality container to create a changes container for.
- # \param new_name The name of the new quality_changes container.
- # \param machine_definition The machine definition this quality changes container is specific to.
- # \param extruder_id
- #
- # \return A new quality_changes container with the specified container as base.
- def _createQualityChanges(self, quality_container, new_name, machine_definition, extruder_id):
- base_id = machine_definition.getId() if extruder_id is None else extruder_id
-
- # Create a new quality_changes container for the quality.
- quality_changes = InstanceContainer(self._createUniqueId(base_id, new_name))
- quality_changes.setName(new_name)
- quality_changes.addMetaDataEntry("type", "quality_changes")
- quality_changes.addMetaDataEntry("quality_type", quality_container.getMetaDataEntry("quality_type"))
-
- # If we are creating a container for an extruder, ensure we add that to the container
- if extruder_id is not None:
- quality_changes.addMetaDataEntry("extruder", extruder_id)
-
- # If the machine specifies qualities should be filtered, ensure we match the current criteria.
- if not machine_definition.getMetaDataEntry("has_machine_quality"):
- quality_changes.setDefinition("fdmprinter")
- else:
- quality_changes.setDefinition(QualityManager.getInstance().getParentMachineDefinition(machine_definition).getId())
-
- from cura.CuraApplication import CuraApplication
- quality_changes.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
- return quality_changes
-
-
- ## Import profiles from a list of file_urls.
- # Each QUrl item must end with .curaprofile, or it will not be imported.
- #
- # \param QVariant, essentially a list with QUrl objects.
- # \return Dict with keys status, text
- @pyqtSlot("QVariantList", result="QVariantMap")
- def importProfiles(self, file_urls):
- status = "ok"
- results = {"ok": [], "error": []}
- for file_url in file_urls:
- if not file_url.isValid():
- continue
- path = file_url.toLocalFile()
- if not path:
- continue
- if not path.endswith(".curaprofile"):
- continue
-
- single_result = self._container_registry.importProfile(path)
- if single_result["status"] == "error":
- status = "error"
- results[single_result["status"]].append(single_result["message"])
-
- return {
- "status": status,
- "message": "\n".join(results["ok"] + results["error"])}
-
## Import single profile, file_url does not have to end with curaprofile
@pyqtSlot(QUrl, result="QVariantMap")
def importProfile(self, file_url):
@@ -1100,11 +458,13 @@ class ContainerManager(QObject):
return
return self._container_registry.importProfile(path)
- @pyqtSlot("QVariantList", QUrl, str)
- def exportProfile(self, instance_id: str, file_url: QUrl, file_type: str) -> None:
+ @pyqtSlot(QObject, QUrl, str)
+ def exportQualityChangesGroup(self, quality_changes_group, file_url: QUrl, file_type: str):
if not file_url.isValid():
return
path = file_url.toLocalFile()
if not path:
return
- self._container_registry.exportProfile(instance_id, path, file_type)
+
+ container_list = [n.getContainer() for n in quality_changes_group.getAllNodes()]
+ self._container_registry.exportQualityProfile(container_list, path, file_type)
diff --git a/cura/Settings/ContainerSettingsModel.py b/cura/Settings/ContainerSettingsModel.py
deleted file mode 100644
index 2c4bef6464..0000000000
--- a/cura/Settings/ContainerSettingsModel.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# Copyright (c) 2016 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-from UM.Application import Application
-from UM.Qt.ListModel import ListModel
-
-from PyQt5.QtCore import pyqtProperty, Qt, pyqtSignal, pyqtSlot, QUrl
-
-from UM.Settings.ContainerRegistry import ContainerRegistry
-from UM.Settings.InstanceContainer import InstanceContainer
-from UM.Settings.SettingFunction import SettingFunction
-
-class ContainerSettingsModel(ListModel):
- LabelRole = Qt.UserRole + 1
- CategoryRole = Qt.UserRole + 2
- UnitRole = Qt.UserRole + 3
- ValuesRole = Qt.UserRole + 4
-
- def __init__(self, parent = None):
- super().__init__(parent)
- self.addRoleName(self.LabelRole, "label")
- self.addRoleName(self.CategoryRole, "category")
- self.addRoleName(self.UnitRole, "unit")
- self.addRoleName(self.ValuesRole, "values")
-
- self._container_ids = []
- self._containers = []
-
- def _onPropertyChanged(self, key, property_name):
- if property_name == "value":
- self._update()
-
- def _update(self):
- items = []
-
- if len(self._container_ids) == 0:
- return
-
- keys = []
- for container in self._containers:
- keys = keys + list(container.getAllKeys())
-
- keys = list(set(keys)) # remove duplicate keys
-
- for key in keys:
- definition = None
- category = None
- values = []
- for container in self._containers:
- instance = container.getInstance(key)
- if instance:
- definition = instance.definition
-
- # Traverse up to find the category
- category = definition
- while category.type != "category":
- category = category.parent
-
- value = container.getProperty(key, "value")
- if type(value) == SettingFunction:
- values.append("=\u0192")
- else:
- values.append(container.getProperty(key, "value"))
- else:
- values.append("")
-
- items.append({
- "key": key,
- "values": values,
- "label": definition.label,
- "unit": definition.unit,
- "category": category.label
- })
- items.sort(key = lambda k: (k["category"], k["key"]))
- self.setItems(items)
-
- ## Set the ids of the containers which have the settings this model should list.
- # Also makes sure the model updates when the containers have property changes
- def setContainers(self, container_ids):
- for container in self._containers:
- container.propertyChanged.disconnect(self._onPropertyChanged)
-
- self._container_ids = container_ids
- self._containers = []
-
- for container_id in self._container_ids:
- containers = ContainerRegistry.getInstance().findContainers(id = container_id)
- if containers:
- containers[0].propertyChanged.connect(self._onPropertyChanged)
- self._containers.append(containers[0])
-
- self._update()
-
- containersChanged = pyqtSignal()
- @pyqtProperty("QVariantList", fset = setContainers, notify = containersChanged)
- def containers(self):
- return self.container_ids
diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py
index 0b328cebda..ab48eaddd2 100644
--- a/cura/Settings/CuraContainerRegistry.py
+++ b/cura/Settings/CuraContainerRegistry.py
@@ -25,14 +25,16 @@ from UM.Resources import Resources
from . import ExtruderStack
from . import GlobalStack
-from .ContainerManager import ContainerManager
from .ExtruderManager import ExtruderManager
from cura.CuraApplication import CuraApplication
+from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
+from cura.ProfileReader import NoProfileException
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
+
class CuraContainerRegistry(ContainerRegistry):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -102,7 +104,7 @@ class CuraContainerRegistry(ContainerRegistry):
# \param instance_ids \type{list} the IDs of the profiles to export.
# \param file_name \type{str} the full path and filename to export to.
# \param file_type \type{str} the file type with the format " (*.)"
- def exportProfile(self, instance_ids, file_name, file_type):
+ def exportQualityProfile(self, container_list, file_name, file_type):
# Parse the fileType to deduce what plugin can save the file format.
# fileType has the format " (*.)"
split = file_type.rfind(" (*.") # Find where the description ends and the extension starts.
@@ -121,31 +123,10 @@ class CuraContainerRegistry(ContainerRegistry):
catalog.i18nc("@label Don't translate the XML tag !", "The file {0} already exists. Are you sure you want to overwrite it?").format(file_name))
if result == QMessageBox.No:
return
- found_containers = []
- extruder_positions = []
- for instance_id in instance_ids:
- containers = ContainerRegistry.getInstance().findInstanceContainers(id = instance_id)
- if containers:
- found_containers.append(containers[0])
-
- # Determine the position of the extruder of this container
- extruder_id = containers[0].getMetaDataEntry("extruder", "")
- if extruder_id == "":
- # Global stack
- extruder_positions.append(-1)
- else:
- extruder_containers = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = extruder_id)
- if extruder_containers:
- extruder_positions.append(int(extruder_containers[0].get("position", 0)))
- else:
- extruder_positions.append(0)
- # Ensure the profiles are always exported in order (global, extruder 0, extruder 1, ...)
- found_containers = [containers for (positions, containers) in sorted(zip(extruder_positions, found_containers))]
profile_writer = self._findProfileWriter(extension, description)
-
try:
- success = profile_writer.write(file_name, found_containers)
+ success = profile_writer.write(file_name, container_list)
except Exception as e:
Logger.log("e", "Failed to export profile to %s: %s", file_name, str(e))
m = Message(catalog.i18nc("@info:status Don't translate the XML tags or !", "Failed to export profile to {0}: {1}", file_name, str(e)),
@@ -192,12 +173,13 @@ class CuraContainerRegistry(ContainerRegistry):
plugin_registry = PluginRegistry.getInstance()
extension = file_name.split(".")[-1]
- global_container_stack = Application.getInstance().getGlobalContainerStack()
- if not global_container_stack:
+ global_stack = Application.getInstance().getGlobalContainerStack()
+ if not global_stack:
return
- machine_extruders = list(ExtruderManager.getInstance().getMachineExtruders(global_container_stack.getId()))
- machine_extruders.sort(key = lambda k: k.getMetaDataEntry("position"))
+ machine_extruders = []
+ for position in sorted(global_stack.extruders):
+ machine_extruders.append(global_stack.extruders[position])
for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
if meta_data["profile_reader"][0]["extension"] != extension:
@@ -205,6 +187,8 @@ class CuraContainerRegistry(ContainerRegistry):
profile_reader = plugin_registry.getPluginObject(plugin_id)
try:
profile_or_list = profile_reader.read(file_name) # Try to open the file with the profile reader.
+ except NoProfileException:
+ return { "status": "ok", "message": catalog.i18nc("@info:status Don't translate the XML tags or !", "No custom profile to import in file {0}", file_name)}
except Exception as e:
# Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None.
Logger.log("e", "Failed to import profile from %s: %s while using profile reader. Got exception %s", file_name,profile_reader.getPluginId(), str(e))
@@ -217,27 +201,50 @@ class CuraContainerRegistry(ContainerRegistry):
# First check if this profile is suitable for this machine
global_profile = None
+ extruder_profiles = []
if len(profile_or_list) == 1:
global_profile = profile_or_list[0]
else:
for profile in profile_or_list:
- if not profile.getMetaDataEntry("extruder"):
+ if not profile.getMetaDataEntry("position"):
global_profile = profile
- break
+ else:
+ extruder_profiles.append(profile)
+ extruder_profiles = sorted(extruder_profiles, key = lambda x: int(x.getMetaDataEntry("position")))
+ profile_or_list = [global_profile] + extruder_profiles
+
if not global_profile:
Logger.log("e", "Incorrect profile [%s]. Could not find global profile", file_name)
return { "status": "error",
"message": catalog.i18nc("@info:status Don't translate the XML tags or !", "This profile {0} contains incorrect data, could not import it.", file_name)}
profile_definition = global_profile.getMetaDataEntry("definition")
- expected_machine_definition = "fdmprinter"
- if parseBool(global_container_stack.getMetaDataEntry("has_machine_quality", "False")):
- expected_machine_definition = global_container_stack.getMetaDataEntry("quality_definition")
- if not expected_machine_definition:
- expected_machine_definition = global_container_stack.definition.getId()
- if expected_machine_definition is not None and profile_definition is not None and profile_definition != expected_machine_definition:
- Logger.log("e", "Profile [%s] is for machine [%s] but the current active machine is [%s]. Will not import the profile", file_name)
+
+ # Make sure we have a profile_definition in the file:
+ if profile_definition is None:
+ break
+ machine_definition = self.findDefinitionContainers(id = profile_definition)
+ if not machine_definition:
+ Logger.log("e", "Incorrect profile [%s]. Unknown machine type [%s]", file_name, profile_definition)
+ return {"status": "error",
+ "message": catalog.i18nc("@info:status Don't translate the XML tags or !", "This profile {0} contains incorrect data, could not import it.", file_name)
+ }
+ machine_definition = machine_definition[0]
+
+ # Get the expected machine definition.
+ # i.e.: We expect gcode for a UM2 Extended to be defined as normal UM2 gcode...
+ profile_definition = getMachineDefinitionIDForQualitySearch(machine_definition)
+ expected_machine_definition = getMachineDefinitionIDForQualitySearch(global_stack.definition)
+
+ # And check if the profile_definition matches either one (showing error if not):
+ if profile_definition != expected_machine_definition:
+ Logger.log("e", "Profile [%s] is for machine [%s] but the current active machine is [%s]. Will not import the profile", file_name, profile_definition, expected_machine_definition)
return { "status": "error",
- "message": catalog.i18nc("@info:status Don't translate the XML tags or !", "The machine defined in profile {0} doesn't match with your current machine, could not import it.", file_name)}
+ "message": catalog.i18nc("@info:status Don't translate the XML tags or !", "The machine defined in profile {0} ({1}) doesn't match with your current machine ({2}), could not import it.", file_name, profile_definition, expected_machine_definition)}
+
+ # Fix the global quality profile's definition field in case it's not correct
+ global_profile.setMetaDataEntry("definition", expected_machine_definition)
+ quality_name = global_profile.getName()
+ quality_type = global_profile.getMetaDataEntry("quality_type")
name_seed = os.path.splitext(os.path.basename(file_name))[0]
new_name = self.uniqueName(name_seed)
@@ -250,25 +257,25 @@ class CuraContainerRegistry(ContainerRegistry):
if len(profile_or_list) == 1:
global_profile = profile_or_list[0]
extruder_profiles = []
- for idx, extruder in enumerate(global_container_stack.extruders.values()):
- profile_id = ContainerRegistry.getInstance().uniqueName(global_container_stack.getId() + "_extruder_" + str(idx + 1))
+ for idx, extruder in enumerate(global_stack.extruders.values()):
+ profile_id = ContainerRegistry.getInstance().uniqueName(global_stack.getId() + "_extruder_" + str(idx + 1))
profile = InstanceContainer(profile_id)
- profile.setName(global_profile.getName())
+ profile.setName(quality_name)
profile.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
profile.addMetaDataEntry("type", "quality_changes")
- profile.addMetaDataEntry("definition", global_profile.getMetaDataEntry("definition"))
- profile.addMetaDataEntry("quality_type", global_profile.getMetaDataEntry("quality_type"))
- profile.addMetaDataEntry("extruder", extruder.getId())
+ profile.addMetaDataEntry("definition", expected_machine_definition)
+ profile.addMetaDataEntry("quality_type", quality_type)
+ profile.addMetaDataEntry("position", "0")
profile.setDirty(True)
if idx == 0:
# move all per-extruder settings to the first extruder's quality_changes
for qc_setting_key in global_profile.getAllKeys():
- settable_per_extruder = global_container_stack.getProperty(qc_setting_key,
+ settable_per_extruder = global_stack.getProperty(qc_setting_key,
"settable_per_extruder")
if settable_per_extruder:
setting_value = global_profile.getProperty(qc_setting_key, "value")
- setting_definition = global_container_stack.getSettingDefinition(qc_setting_key)
+ setting_definition = global_stack.getSettingDefinition(qc_setting_key)
new_instance = SettingInstance(setting_definition, profile)
new_instance.setProperty("value", setting_value)
new_instance.resetState() # Ensure that the state is not seen as a user state.
@@ -285,21 +292,22 @@ class CuraContainerRegistry(ContainerRegistry):
for profile_index, profile in enumerate(profile_or_list):
if profile_index == 0:
# This is assumed to be the global profile
- profile_id = (global_container_stack.getBottom().getId() + "_" + name_seed).lower().replace(" ", "_")
+ profile_id = (global_stack.getBottom().getId() + "_" + name_seed).lower().replace(" ", "_")
elif profile_index < len(machine_extruders) + 1:
# This is assumed to be an extruder profile
- extruder_id = Application.getInstance().getMachineManager().getQualityDefinitionId(machine_extruders[profile_index - 1].getBottom())
- if not profile.getMetaDataEntry("extruder"):
- profile.addMetaDataEntry("extruder", extruder_id)
+ extruder_id = machine_extruders[profile_index - 1].definition.getId()
+ extruder_position = str(profile_index - 1)
+ if not profile.getMetaDataEntry("position"):
+ profile.addMetaDataEntry("position", extruder_position)
else:
- profile.setMetaDataEntry("extruder", extruder_id)
+ profile.setMetaDataEntry("position", extruder_position)
profile_id = (extruder_id + "_" + name_seed).lower().replace(" ", "_")
else: #More extruders in the imported file than in the machine.
continue #Delete the additional profiles.
- result = self._configureProfile(profile, profile_id, new_name)
+ result = self._configureProfile(profile, profile_id, new_name, expected_machine_definition)
if result is not None:
return {"status": "error", "message": catalog.i18nc(
"@info:status Don't translate the XML tags or !",
@@ -327,7 +335,7 @@ class CuraContainerRegistry(ContainerRegistry):
# \param new_name The new name for the profile.
#
# \return None if configuring was successful or an error message if an error occurred.
- def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str) -> Optional[str]:
+ def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str, machine_definition_id: str) -> Optional[str]:
profile.setDirty(True) # Ensure the profiles are correctly saved
new_id = self.createUniqueName("quality_changes", "", id_seed, catalog.i18nc("@label", "Custom profile"))
@@ -337,6 +345,7 @@ class CuraContainerRegistry(ContainerRegistry):
# Set the unique Id to the profile, so it's generating a new one even if the user imports the same profile
# It also solves an issue with importing profiles from G-Codes
profile.setMetaDataEntry("id", new_id)
+ profile.setMetaDataEntry("definition", machine_definition_id)
if "type" in profile.getMetaData():
profile.setMetaDataEntry("type", "quality_changes")
@@ -347,40 +356,16 @@ class CuraContainerRegistry(ContainerRegistry):
if not quality_type:
return catalog.i18nc("@info:status", "Profile is missing a quality type.")
- quality_type_criteria = {"quality_type": quality_type}
- if self._machineHasOwnQualities():
- profile.setDefinition(self._activeQualityDefinition().getId())
- if self._machineHasOwnMaterials():
- active_material_id = self._activeMaterialId()
- if active_material_id and active_material_id != "empty": # only update if there is an active material
- profile.addMetaDataEntry("material", active_material_id)
- quality_type_criteria["material"] = active_material_id
-
- quality_type_criteria["definition"] = profile.getDefinition().getId()
-
- else:
- profile.setDefinition("fdmprinter")
- quality_type_criteria["definition"] = "fdmprinter"
-
- machine_definition = Application.getInstance().getGlobalContainerStack().getBottom()
- del quality_type_criteria["definition"]
-
- # materials = None
-
- if "material" in quality_type_criteria:
- # materials = ContainerRegistry.getInstance().findInstanceContainers(id = quality_type_criteria["material"])
- del quality_type_criteria["material"]
-
- # Do not filter quality containers here with materials because we are trying to import a profile, so it should
- # NOT be restricted by the active materials on the current machine.
- materials = None
+ global_stack = Application.getInstance().getGlobalContainerStack()
+ 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.
- from cura.QualityManager import QualityManager
- qualities = QualityManager.getInstance()._getFilteredContainersForStack(machine_definition, materials, **quality_type_criteria)
- if not qualities:
+ quality_manager = 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)
ContainerRegistry.getInstance().addContainer(profile)
@@ -400,18 +385,6 @@ class CuraContainerRegistry(ContainerRegistry):
result.append( (plugin_id, meta_data) )
return result
- ## Get the definition to use to select quality profiles for the active machine
- # \return the active quality definition object or None if there is no quality definition
- def _activeQualityDefinition(self):
- global_container_stack = Application.getInstance().getGlobalContainerStack()
- if global_container_stack:
- definition_id = Application.getInstance().getMachineManager().getQualityDefinitionId(global_container_stack.getBottom())
- definition = self.findDefinitionContainers(id = definition_id)[0]
-
- if definition:
- return definition
- return None
-
## Returns true if the current machine requires its own materials
# \return True if the current machine requires its own materials
def _machineHasOwnMaterials(self):
@@ -507,8 +480,6 @@ class CuraContainerRegistry(ContainerRegistry):
extruder_stack.setDefinition(extruder_definition)
extruder_stack.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
- from cura.CuraApplication import CuraApplication
-
# create a new definition_changes container for the extruder stack
definition_changes_id = self.uniqueName(extruder_stack.getId() + "_settings") if create_new_ids else extruder_stack.getId() + "_settings"
definition_changes_name = definition_changes_id
@@ -567,26 +538,28 @@ class CuraContainerRegistry(ContainerRegistry):
self.addContainer(user_container)
extruder_stack.setUserChanges(user_container)
- variant_id = "default"
+ application = CuraApplication.getInstance()
+ empty_variant = application.empty_variant_container
+ empty_material = application.empty_material_container
+ empty_quality = application.empty_quality_container
+
if machine.variant.getId() not in ("empty", "empty_variant"):
- variant_id = machine.variant.getId()
+ variant = machine.variant
else:
- variant_id = "empty_variant"
- extruder_stack.setVariantById(variant_id)
+ variant = empty_variant
+ extruder_stack.variant = variant
- material_id = "default"
if machine.material.getId() not in ("empty", "empty_material"):
- material_id = machine.material.getId()
+ material = machine.material
else:
- material_id = "empty_material"
- extruder_stack.setMaterialById(material_id)
+ material = empty_material
+ extruder_stack.material = material
- quality_id = "default"
if machine.quality.getId() not in ("empty", "empty_quality"):
- quality_id = machine.quality.getId()
+ quality = machine.quality
else:
- quality_id = "empty_quality"
- extruder_stack.setQualityById(quality_id)
+ quality = empty_quality
+ extruder_stack.quality = quality
machine_quality_changes = machine.qualityChanges
if new_global_quality_changes is not None:
@@ -598,7 +571,7 @@ class CuraContainerRegistry(ContainerRegistry):
extruder_quality_changes_container = extruder_quality_changes_container[0]
quality_changes_id = extruder_quality_changes_container.getId()
- extruder_stack.setQualityChangesById(quality_changes_id)
+ extruder_stack.qualityChanges = self.findInstanceContainers(id = quality_changes_id)[0]
else:
# Some extruder quality_changes containers can be created at runtime as files in the qualities
# folder. Those files won't be loaded in the registry immediately. So we also need to search
@@ -606,7 +579,8 @@ class CuraContainerRegistry(ContainerRegistry):
extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine_quality_changes.getName())
if extruder_quality_changes_container:
quality_changes_id = extruder_quality_changes_container.getId()
- extruder_stack.setQualityChangesById(quality_changes_id)
+ extruder_quality_changes_container.addMetaDataEntry("extruder", extruder_stack.definition.getId())
+ extruder_stack.qualityChanges = self.findInstanceContainers(id = quality_changes_id)[0]
else:
# if we still cannot find a quality changes container for the extruder, create a new one
container_name = machine_quality_changes.getName()
@@ -641,7 +615,7 @@ class CuraContainerRegistry(ContainerRegistry):
machine_quality_changes.removeInstance(qc_setting_key, postpone_emit=True)
else:
- extruder_stack.setQualityChangesById("empty_quality_changes")
+ extruder_stack.qualityChanges = self.findInstanceContainers(id = "empty_quality_changes")[0]
self.addContainer(extruder_stack)
@@ -711,7 +685,7 @@ class CuraContainerRegistry(ContainerRegistry):
if not os.path.isfile(file_path):
continue
- parser = configparser.ConfigParser()
+ parser = configparser.ConfigParser(interpolation=None)
try:
parser.read([file_path])
except:
diff --git a/cura/Settings/CuraContainerStack.py b/cura/Settings/CuraContainerStack.py
index b97bb3314e..ca4f866598 100755
--- a/cura/Settings/CuraContainerStack.py
+++ b/cura/Settings/CuraContainerStack.py
@@ -8,6 +8,7 @@ from typing import Any, Optional
from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject
from UM.FlameProfiler import pyqtSlot
+from UM.Application import Application
from UM.Decorators import override
from UM.Logger import Logger
from UM.Settings.ContainerStack import ContainerStack, InvalidContainerStackError
@@ -83,20 +84,6 @@ class CuraContainerStack(ContainerStack):
def setQualityChanges(self, new_quality_changes: InstanceContainer, postpone_emit = False) -> None:
self.replaceContainer(_ContainerIndexes.QualityChanges, new_quality_changes, postpone_emit = postpone_emit)
- ## Set the quality changes container by an ID.
- #
- # This will search for the specified container and set it. If no container was found, an error will be raised.
- #
- # \param new_quality_changes_id The ID of the new quality changes container.
- #
- # \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
- def setQualityChangesById(self, new_quality_changes_id: str) -> None:
- quality_changes = ContainerRegistry.getInstance().findInstanceContainers(id = new_quality_changes_id)
- if quality_changes:
- self.setQualityChanges(quality_changes[0])
- else:
- raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_quality_changes_id))
-
## Get the quality changes container.
#
# \return The quality changes container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@@ -110,31 +97,6 @@ class CuraContainerStack(ContainerStack):
def setQuality(self, new_quality: InstanceContainer, postpone_emit = False) -> None:
self.replaceContainer(_ContainerIndexes.Quality, new_quality, postpone_emit = postpone_emit)
- ## Set the quality container by an ID.
- #
- # This will search for the specified container and set it. If no container was found, an error will be raised.
- # There is a special value for ID, which is "default". The "default" value indicates the quality should be set
- # to whatever the machine definition specifies as "preferred" container, or a fallback value. See findDefaultQuality
- # for details.
- #
- # \param new_quality_id The ID of the new quality container.
- #
- # \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
- def setQualityById(self, new_quality_id: str) -> None:
- quality = self._empty_quality
- if new_quality_id == "default":
- new_quality = self.findDefaultQuality()
- if new_quality:
- quality = new_quality
- else:
- qualities = ContainerRegistry.getInstance().findInstanceContainers(id = new_quality_id)
- if qualities:
- quality = qualities[0]
- else:
- raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_quality_id))
-
- self.setQuality(quality)
-
## Get the quality container.
#
# \return The quality container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@@ -148,31 +110,6 @@ class CuraContainerStack(ContainerStack):
def setMaterial(self, new_material: InstanceContainer, postpone_emit = False) -> None:
self.replaceContainer(_ContainerIndexes.Material, new_material, postpone_emit = postpone_emit)
- ## Set the material container by an ID.
- #
- # This will search for the specified container and set it. If no container was found, an error will be raised.
- # There is a special value for ID, which is "default". The "default" value indicates the quality should be set
- # to whatever the machine definition specifies as "preferred" container, or a fallback value. See findDefaultMaterial
- # for details.
- #
- # \param new_material_id The ID of the new material container.
- #
- # \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
- def setMaterialById(self, new_material_id: str) -> None:
- material = self._empty_material
- if new_material_id == "default":
- new_material = self.findDefaultMaterial()
- if new_material:
- material = new_material
- else:
- materials = ContainerRegistry.getInstance().findInstanceContainers(id = new_material_id)
- if materials:
- material = materials[0]
- else:
- raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_material_id))
-
- self.setMaterial(material)
-
## Get the material container.
#
# \return The material container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@@ -186,31 +123,6 @@ class CuraContainerStack(ContainerStack):
def setVariant(self, new_variant: InstanceContainer) -> None:
self.replaceContainer(_ContainerIndexes.Variant, new_variant)
- ## Set the variant container by an ID.
- #
- # This will search for the specified container and set it. If no container was found, an error will be raised.
- # There is a special value for ID, which is "default". The "default" value indicates the quality should be set
- # to whatever the machine definition specifies as "preferred" container, or a fallback value. See findDefaultVariant
- # for details.
- #
- # \param new_variant_id The ID of the new variant container.
- #
- # \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
- def setVariantById(self, new_variant_id: str) -> None:
- variant = self._empty_variant
- if new_variant_id == "default":
- new_variant = self.findDefaultVariantBuildplate() if self.getMetaDataEntry("type") == "machine" else self.findDefaultVariant()
- if new_variant:
- variant = new_variant
- else:
- variants = ContainerRegistry.getInstance().findInstanceContainers(id = new_variant_id)
- if variants:
- variant = variants[0]
- else:
- raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_variant_id))
-
- self.setVariant(variant)
-
## Get the variant container.
#
# \return The variant container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@@ -224,18 +136,6 @@ class CuraContainerStack(ContainerStack):
def setDefinitionChanges(self, new_definition_changes: InstanceContainer) -> None:
self.replaceContainer(_ContainerIndexes.DefinitionChanges, new_definition_changes)
- ## Set the definition changes container by an ID.
- #
- # \param new_definition_changes_id The ID of the new definition changes container.
- #
- # \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
- def setDefinitionChangesById(self, new_definition_changes_id: str) -> None:
- new_definition_changes = ContainerRegistry.getInstance().findInstanceContainers(id = new_definition_changes_id)
- if new_definition_changes:
- self.setDefinitionChanges(new_definition_changes[0])
- else:
- raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_definition_changes_id))
-
## Get the definition changes container.
#
# \return The definition changes container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@@ -249,18 +149,6 @@ class CuraContainerStack(ContainerStack):
def setDefinition(self, new_definition: DefinitionContainerInterface) -> None:
self.replaceContainer(_ContainerIndexes.Definition, new_definition)
- ## Set the definition container by an ID.
- #
- # \param new_definition_id The ID of the new definition container.
- #
- # \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
- def setDefinitionById(self, new_definition_id: str) -> None:
- new_definition = ContainerRegistry.getInstance().findDefinitionContainers(id = new_definition_id)
- if new_definition:
- self.setDefinition(new_definition[0])
- else:
- raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_definition_id))
-
## Get the definition container.
#
# \return The definition container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@@ -348,6 +236,10 @@ class CuraContainerStack(ContainerStack):
elif container != self._empty_instance_container and container.getMetaDataEntry("type") != expected_type:
raise Exceptions.InvalidContainerError("Cannot replace container at index {index} with a container that is not of {type} type, but {actual_type} type.".format(index = index, type = expected_type, actual_type = container.getMetaDataEntry("type")))
+ current_container = self._containers[index]
+ if current_container.getId() == container.getId():
+ return
+
super().replaceContainer(index, container, postpone_emit)
## Overridden from ContainerStack
@@ -391,243 +283,6 @@ class CuraContainerStack(ContainerStack):
self._containers = new_containers
- ## Find the variant that should be used as "default" variant.
- #
- # This will search for variants that match the current definition and pick the preferred one,
- # if specified by the machine definition.
- #
- # The following criteria are used to find the default variant:
- # - If the machine definition does not have a metadata entry "has_variants" set to True, return None
- # - The definition of the variant should be the same as the machine definition for this stack.
- # - The container should have a metadata entry "type" with value "variant".
- # - If the machine definition has a metadata entry "preferred_variant", filter the variant IDs based on that.
- #
- # \return The container that should be used as default, or None if nothing was found or the machine does not use variants.
- #
- # \note This method assumes the stack has a valid machine definition.
- def findDefaultVariant(self) -> Optional[ContainerInterface]:
- definition = self._getMachineDefinition()
- # has_variants can be overridden in other containers and stacks.
- # In the case of UM2, it is overridden in the GlobalStack
- if not self.getMetaDataEntry("has_variants"):
- # If the machine does not use variants, we should never set a variant.
- return None
-
- # First add any variant. Later, overwrite with preference if the preference is valid.
- variant = None
- definition_id = self._findInstanceContainerDefinitionId(definition)
- variants = ContainerRegistry.getInstance().findInstanceContainers(definition = definition_id, type = "variant")
- if variants:
- variant = variants[0]
-
- preferred_variant_id = definition.getMetaDataEntry("preferred_variant")
- if preferred_variant_id:
- preferred_variants = ContainerRegistry.getInstance().findInstanceContainers(id = preferred_variant_id, definition = definition_id, type = "variant")
- if preferred_variants:
- variant = preferred_variants[0]
- else:
- Logger.log("w", "The preferred variant \"{variant}\" of stack {stack} does not exist or is not a variant.", variant = preferred_variant_id, stack = self.id)
- # And leave it at the default variant.
-
- if variant:
- return variant
-
- Logger.log("w", "Could not find a valid default variant for stack {stack}", stack = self.id)
- return None
-
- ## Find the global variant that should be used as "default". This is used for the buildplates.
- #
- # This will search for variants that match the current definition and pick the preferred one,
- # if specified by the machine definition.
- #
- # The following criteria are used to find the default global variant:
- # - If the machine definition does not have a metadata entry "has_variant_buildplates" set to True, return None
- # - The definition of the variant should be the same as the machine definition for this stack.
- # - The container should have a metadata entry "type" with value "variant" and "hardware_type" with value "buildplate".
- # - If the machine definition has a metadata entry "preferred_variant_buildplate", filter the variant IDs based on that.
- #
- # \return The container that should be used as default, or None if nothing was found or the machine does not use variants.
- #
- # \note This method assumes the stack has a valid machine definition.
- def findDefaultVariantBuildplate(self) -> Optional[ContainerInterface]:
- definition = self._getMachineDefinition()
- # has_variant_buildplates can be overridden in other containers and stacks.
- # In the case of UM2, it is overridden in the GlobalStack
- if not self.getMetaDataEntry("has_variant_buildplates"):
- # If the machine does not use variants, we should never set a variant.
- return None
-
- # First add any variant. Later, overwrite with preference if the preference is valid.
- variant = None
- definition_id = self._findInstanceContainerDefinitionId(definition)
- variants = ContainerRegistry.getInstance().findInstanceContainers(definition = definition_id, type = "variant", hardware_type = "buildplate")
- if variants:
- variant = variants[0]
-
- preferred_variant_buildplate_id = definition.getMetaDataEntry("preferred_variant_buildplate")
- if preferred_variant_buildplate_id:
- preferred_variant_buildplates = ContainerRegistry.getInstance().findInstanceContainers(id = preferred_variant_buildplate_id, definition = definition_id, type = "variant")
- if preferred_variant_buildplates:
- variant = preferred_variant_buildplates[0]
- else:
- Logger.log("w", "The preferred variant buildplate \"{variant}\" of stack {stack} does not exist or is not a variant.",
- variant = preferred_variant_buildplate_id, stack = self.id)
- # And leave it at the default variant.
-
- if variant:
- return variant
-
- Logger.log("w", "Could not find a valid default buildplate variant for stack {stack}", stack = self.id)
- return None
-
- ## Find the material that should be used as "default" material.
- #
- # This will search for materials that match the current definition and pick the preferred one,
- # if specified by the machine definition.
- #
- # The following criteria are used to find the default material:
- # - If the machine definition does not have a metadata entry "has_materials" set to True, return None
- # - If the machine definition has a metadata entry "has_machine_materials", the definition of the material should
- # be the same as the machine definition for this stack. Otherwise, the definition should be "fdmprinter".
- # - The container should have a metadata entry "type" with value "material".
- # - The material should have an approximate diameter that matches the machine
- # - If the machine definition has a metadata entry "has_variants" and set to True, the "variant" metadata entry of
- # the material should be the same as the ID of the variant in the stack. Only applies if "has_machine_materials" is also True.
- # - If the stack currently has a material set, try to find a material that matches the current material by name.
- # - Otherwise, if the machine definition has a metadata entry "preferred_material", try to find a material that matches the specified ID.
- #
- # \return The container that should be used as default, or None if nothing was found or the machine does not use materials.
- def findDefaultMaterial(self) -> Optional[ContainerInterface]:
- definition = self._getMachineDefinition()
- if not definition.getMetaDataEntry("has_materials"):
- # Machine does not use materials, never try to set it.
- return None
-
- search_criteria = {"type": "material"}
- if definition.getMetaDataEntry("has_machine_materials"):
- search_criteria["definition"] = self._findInstanceContainerDefinitionId(definition)
-
- if definition.getMetaDataEntry("has_variants"):
- search_criteria["variant"] = self.variant.id
- else:
- search_criteria["definition"] = "fdmprinter"
-
- if self.material != self._empty_material:
- search_criteria["name"] = self.material.name
- else:
- preferred_material = definition.getMetaDataEntry("preferred_material")
- if preferred_material:
- search_criteria["id"] = preferred_material
-
- approximate_material_diameter = str(round(self.getProperty("material_diameter", "value")))
- search_criteria["approximate_diameter"] = approximate_material_diameter
-
- materials = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
- if not materials:
- Logger.log("w", "The preferred material \"{material}\" could not be found for stack {stack}", material = preferred_material, stack = self.id)
- # We failed to find any materials matching the specified criteria, drop some specific criteria and try to find
- # a material that sort-of matches what we want.
- search_criteria.pop("variant", None)
- search_criteria.pop("id", None)
- search_criteria.pop("name", None)
- materials = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
-
- if not materials:
- Logger.log("w", "Could not find a valid material for stack {stack}", stack = self.id)
- return None
-
- for material in materials:
- # Prefer a read-only material
- if ContainerRegistry.getInstance().isReadOnly(material.getId()):
- return material
-
- return materials[0]
-
-
- ## Find the quality that should be used as "default" quality.
- #
- # This will search for qualities that match the current definition and pick the preferred one,
- # if specified by the machine definition.
- #
- # \return The container that should be used as default, or None if nothing was found.
- def findDefaultQuality(self) -> Optional[ContainerInterface]:
- definition = self._getMachineDefinition()
- registry = ContainerRegistry.getInstance()
- material_container = self.material if self.material.getId() not in (self._empty_material.getId(), self._empty_instance_container.getId()) else None
-
- search_criteria = {"type": "quality"}
-
- if definition.getMetaDataEntry("has_machine_quality"):
- search_criteria["definition"] = self._findInstanceContainerDefinitionId(definition)
-
- if definition.getMetaDataEntry("has_materials") and material_container:
- search_criteria["material"] = material_container.id
- else:
- search_criteria["definition"] = "fdmprinter"
-
- if self.quality != self._empty_quality:
- search_criteria["name"] = self.quality.name
- else:
- preferred_quality = definition.getMetaDataEntry("preferred_quality")
- if preferred_quality:
- search_criteria["id"] = preferred_quality
-
- containers = registry.findInstanceContainers(**search_criteria)
- if containers:
- return containers[0]
-
- if "material" in search_criteria:
- # First check if we can solve our material not found problem by checking if we can find quality containers
- # that are assigned to the parents of this material profile.
- try:
- inherited_files = material_container.getInheritedFiles()
- except AttributeError: # Material_container does not support inheritance.
- inherited_files = []
-
- if inherited_files:
- for inherited_file in inherited_files:
- # Extract the ID from the path we used to load the file.
- search_criteria["material"] = os.path.basename(inherited_file).split(".")[0]
- containers = registry.findInstanceContainers(**search_criteria)
- if containers:
- return containers[0]
-
- # We still weren't able to find a quality for this specific material.
- # Try to find qualities for a generic version of the material.
- material_search_criteria = {"type": "material", "material": material_container.getMetaDataEntry("material"), "color_name": "Generic"}
- if definition.getMetaDataEntry("has_machine_quality"):
- if self.material != self._empty_instance_container:
- material_search_criteria["definition"] = material_container.getMetaDataEntry("definition")
-
- if definition.getMetaDataEntry("has_variants"):
- material_search_criteria["variant"] = material_container.getMetaDataEntry("variant")
- else:
- material_search_criteria["definition"] = self._findInstanceContainerDefinitionId(definition)
-
- if definition.getMetaDataEntry("has_variants") and self.variant != self._empty_instance_container:
- material_search_criteria["variant"] = self.variant.id
- else:
- material_search_criteria["definition"] = "fdmprinter"
- material_containers = registry.findInstanceContainersMetadata(**material_search_criteria)
- # Try all materials to see if there is a quality profile available.
- for material_container in material_containers:
- search_criteria["material"] = material_container["id"]
-
- containers = registry.findInstanceContainers(**search_criteria)
- if containers:
- return containers[0]
-
- if "name" in search_criteria or "id" in search_criteria:
- # If a quality by this name can not be found, try a wider set of search criteria
- search_criteria.pop("name", None)
- search_criteria.pop("id", None)
-
- containers = registry.findInstanceContainers(**search_criteria)
- if containers:
- return containers[0]
-
- return None
-
## protected:
# Helper to make sure we emit a PyQt signal on container changes.
@@ -660,6 +315,13 @@ class CuraContainerStack(ContainerStack):
return cls._findInstanceContainerDefinitionId(definitions[0])
+ ## getProperty for extruder positions, with translation from -1 to default extruder number
+ def getExtruderPositionValueWithDefault(self, key):
+ value = self.getProperty(key, "value")
+ if value == -1:
+ value = int(Application.getInstance().getMachineManager().defaultExtruderPosition)
+ return value
+
## private:
# Private helper class to keep track of container positions and their types.
diff --git a/cura/Settings/CuraStackBuilder.py b/cura/Settings/CuraStackBuilder.py
index 608a13c600..a8234b9de9 100644
--- a/cura/Settings/CuraStackBuilder.py
+++ b/cura/Settings/CuraStackBuilder.py
@@ -1,15 +1,18 @@
-# Copyright (c) 2017 Ultimaker B.V.
+# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
-from UM.Logger import Logger
+from typing import Optional
+from UM.Logger import Logger
from UM.Settings.Interfaces import DefinitionContainerInterface
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.ContainerRegistry import ContainerRegistry
+from UM.Settings.SettingFunction import SettingFunction
+from UM.Util import parseBool
+from cura.Machines.VariantManager import VariantType
from .GlobalStack import GlobalStack
from .ExtruderStack import ExtruderStack
-from typing import Optional
## Contains helper functions to create new machines.
@@ -22,7 +25,13 @@ class CuraStackBuilder:
# \return The new global stack or None if an error occurred.
@classmethod
def createMachine(cls, name: str, definition_id: str) -> Optional[GlobalStack]:
+ from cura.CuraApplication import CuraApplication
+ application = CuraApplication.getInstance()
+ variant_manager = application.getVariantManager()
+ material_manager = application.getMaterialManager()
+ quality_manager = application.getQualityManager()
registry = ContainerRegistry.getInstance()
+
definitions = registry.findDefinitionContainers(id = definition_id)
if not definitions:
Logger.log("w", "Definition {definition} was not found!", definition = definition_id)
@@ -30,7 +39,21 @@ class CuraStackBuilder:
machine_definition = definitions[0]
- generated_name = registry.createUniqueName("machine", "", name, machine_definition.name)
+ # get variant container for the global stack
+ global_variant_container = application.empty_variant_container
+ global_variant_node = variant_manager.getDefaultVariantNode(machine_definition, VariantType.BUILD_PLATE)
+ if global_variant_node:
+ global_variant_container = global_variant_node.getContainer()
+
+ # get variant container for extruders
+ extruder_variant_container = application.empty_variant_container
+ extruder_variant_node = variant_manager.getDefaultVariantNode(machine_definition, VariantType.NOZZLE)
+ extruder_variant_name = None
+ if extruder_variant_node:
+ extruder_variant_container = extruder_variant_node.getContainer()
+ extruder_variant_name = extruder_variant_container.getName()
+
+ generated_name = registry.createUniqueName("machine", "", name, machine_definition.getName())
# Make sure the new name does not collide with any definition or (quality) profile
# createUniqueName() only looks at other stacks, but not at definitions or quality profiles
# Note that we don't go for uniqueName() immediately because that function matches with ignore_case set to true
@@ -40,49 +63,55 @@ class CuraStackBuilder:
new_global_stack = cls.createGlobalStack(
new_stack_id = generated_name,
definition = machine_definition,
- quality = "default",
- material = "default",
- variant = "default",
+ variant_container = global_variant_container,
+ material_container = application.empty_material_container,
+ quality_container = application.empty_quality_container,
)
-
new_global_stack.setName(generated_name)
- extruder_definition = registry.findDefinitionContainers(machine = machine_definition.getId())
+ # get material container for extruders
+ material_container = application.empty_material_container
+ material_node = material_manager.getDefaultMaterial(new_global_stack, extruder_variant_name)
+ if material_node:
+ material_container = material_node.getContainer()
- if not extruder_definition:
- # create extruder stack for single extrusion machines that have no separate extruder definition files
- extruder_definition = registry.findDefinitionContainers(id = "fdmextruder")[0]
- new_extruder_id = registry.uniqueName(machine_definition.getName() + " " + extruder_definition.id)
+ # Create ExtruderStacks
+ extruder_dict = machine_definition.getMetaDataEntry("machine_extruder_trains")
+
+ for position, extruder_definition_id in extruder_dict.items():
+ # Sanity check: make sure that the positions in the extruder definitions are same as in the machine
+ # definition
+ extruder_definition = registry.findDefinitionContainers(id = extruder_definition_id)[0]
+ position_in_extruder_def = extruder_definition.getMetaDataEntry("position")
+ if position_in_extruder_def != position:
+ raise RuntimeError("Extruder position [%s] defined in extruder definition [%s] is not the same as in machine definition [%s] position [%s]" %
+ (position_in_extruder_def, extruder_definition_id, definition_id, position))
+
+ new_extruder_id = registry.uniqueName(extruder_definition_id)
new_extruder = cls.createExtruderStack(
new_extruder_id,
- definition = extruder_definition,
- machine_definition_id = machine_definition.getId(),
- quality = "default",
- material = "default",
- variant = "default",
- next_stack = new_global_stack
+ extruder_definition = extruder_definition,
+ machine_definition_id = definition_id,
+ position = position,
+ variant_container = extruder_variant_container,
+ material_container = material_container,
+ quality_container = application.empty_quality_container,
+ global_stack = new_global_stack,
)
+ new_extruder.setNextStack(new_global_stack)
new_global_stack.addExtruder(new_extruder)
registry.addContainer(new_extruder)
- else:
- # create extruder stack for each found extruder definition
- for extruder_definition in registry.findDefinitionContainers(machine = machine_definition.id):
- position = extruder_definition.getMetaDataEntry("position", None)
- if not position:
- Logger.log("w", "Extruder definition %s specifies no position metadata entry.", extruder_definition.id)
- new_extruder_id = registry.uniqueName(extruder_definition.id)
- new_extruder = cls.createExtruderStack(
- new_extruder_id,
- definition = extruder_definition,
- machine_definition_id = machine_definition.getId(),
- quality = "default",
- material = "default",
- variant = "default",
- next_stack = new_global_stack
- )
- new_global_stack.addExtruder(new_extruder)
- registry.addContainer(new_extruder)
+ preferred_quality_type = machine_definition.getMetaDataEntry("preferred_quality_type")
+ quality_group_dict = quality_manager.getQualityGroups(new_global_stack)
+ quality_group = quality_group_dict.get(preferred_quality_type)
+
+ new_global_stack.quality = quality_group.node_for_global.getContainer()
+ for position, extruder_stack in new_global_stack.extruders.items():
+ if position in quality_group.nodes_for_extruders:
+ extruder_stack.quality = quality_group.nodes_for_extruders[position].getContainer()
+ else:
+ extruder_stack.quality = application.empty_quality_container
# Register the global stack after the extruder stacks are created. This prevents the registry from adding another
# extruder stack because the global stack didn't have one yet (which is enforced since Cura 3.1).
@@ -100,43 +129,27 @@ class CuraStackBuilder:
#
# \return A new Global stack instance with the specified parameters.
@classmethod
- def createExtruderStack(cls, new_stack_id: str, definition: DefinitionContainerInterface, machine_definition_id: str, **kwargs) -> ExtruderStack:
- stack = ExtruderStack(new_stack_id)
- stack.setName(definition.getName())
- stack.setDefinition(definition)
- stack.addMetaDataEntry("position", definition.getMetaDataEntry("position"))
-
- if "next_stack" in kwargs:
- # Add stacks before containers are added, since they may trigger a setting update.
- stack.setNextStack(kwargs["next_stack"])
-
- user_container = InstanceContainer(new_stack_id + "_user")
- user_container.addMetaDataEntry("type", "user")
- user_container.addMetaDataEntry("extruder", new_stack_id)
+ def createExtruderStack(cls, new_stack_id: str, extruder_definition: DefinitionContainerInterface, machine_definition_id: str,
+ position: int,
+ variant_container, material_container, quality_container, global_stack) -> ExtruderStack:
from cura.CuraApplication import CuraApplication
- user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
- user_container.setDefinition(machine_definition_id)
+ application = CuraApplication.getInstance()
- stack.setUserChanges(user_container)
+ stack = ExtruderStack(new_stack_id, parent = global_stack)
+ stack.setName(extruder_definition.getName())
+ stack.setDefinition(extruder_definition)
- # Important! The order here matters, because that allows the stack to
- # assume the material and variant have already been set.
- if "definition_changes" in kwargs:
- stack.setDefinitionChangesById(kwargs["definition_changes"])
- else:
- stack.setDefinitionChanges(cls.createDefinitionChangesContainer(stack, new_stack_id + "_settings"))
+ stack.addMetaDataEntry("position", position)
- if "variant" in kwargs:
- stack.setVariantById(kwargs["variant"])
+ user_container = cls.createUserChangesContainer(new_stack_id + "_user", machine_definition_id, new_stack_id,
+ is_global_stack = False)
- if "material" in kwargs:
- stack.setMaterialById(kwargs["material"])
-
- if "quality" in kwargs:
- stack.setQualityById(kwargs["quality"])
-
- if "quality_changes" in kwargs:
- stack.setQualityChangesById(kwargs["quality_changes"])
+ stack.definitionChanges = cls.createDefinitionChangesContainer(stack, new_stack_id + "_settings")
+ stack.variant = variant_container
+ stack.material = material_container
+ stack.quality = quality_container
+ stack.qualityChanges = application.empty_quality_changes_container
+ stack.userChanges = user_container
# Only add the created containers to the registry after we have set all the other
# properties. This makes the create operation more transactional, since any problems
@@ -153,44 +166,48 @@ class CuraStackBuilder:
#
# \return A new Global stack instance with the specified parameters.
@classmethod
- def createGlobalStack(cls, new_stack_id: str, definition: DefinitionContainerInterface, **kwargs) -> GlobalStack:
+ def createGlobalStack(cls, new_stack_id: str, definition: DefinitionContainerInterface,
+ variant_container, material_container, quality_container) -> GlobalStack:
+ from cura.CuraApplication import CuraApplication
+ application = CuraApplication.getInstance()
+
stack = GlobalStack(new_stack_id)
stack.setDefinition(definition)
- user_container = InstanceContainer(new_stack_id + "_user")
- user_container.addMetaDataEntry("type", "user")
- user_container.addMetaDataEntry("machine", new_stack_id)
- from cura.CuraApplication import CuraApplication
- user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
- user_container.setDefinition(definition.getId())
+ # Create user container
+ user_container = cls.createUserChangesContainer(new_stack_id + "_user", definition.getId(), new_stack_id,
+ is_global_stack = True)
- stack.setUserChanges(user_container)
-
- # Important! The order here matters, because that allows the stack to
- # assume the material and variant have already been set.
- if "definition_changes" in kwargs:
- stack.setDefinitionChangesById(kwargs["definition_changes"])
- else:
- stack.setDefinitionChanges(cls.createDefinitionChangesContainer(stack, new_stack_id + "_settings"))
-
- if "variant" in kwargs:
- stack.setVariantById(kwargs["variant"])
-
- if "material" in kwargs:
- stack.setMaterialById(kwargs["material"])
-
- if "quality" in kwargs:
- stack.setQualityById(kwargs["quality"])
-
- if "quality_changes" in kwargs:
- stack.setQualityChangesById(kwargs["quality_changes"])
+ stack.definitionChanges = cls.createDefinitionChangesContainer(stack, new_stack_id + "_settings")
+ stack.variant = variant_container
+ stack.material = material_container
+ stack.quality = quality_container
+ stack.qualityChanges = application.empty_quality_changes_container
+ stack.userChanges = user_container
ContainerRegistry.getInstance().addContainer(user_container)
return stack
@classmethod
- def createDefinitionChangesContainer(cls, container_stack, container_name, container_index = None):
+ def createUserChangesContainer(cls, container_name: str, definition_id: str, stack_id: str,
+ is_global_stack: bool) -> "InstanceContainer":
+ from cura.CuraApplication import CuraApplication
+
+ unique_container_name = ContainerRegistry.getInstance().uniqueName(container_name)
+
+ container = InstanceContainer(unique_container_name)
+ container.setDefinition(definition_id)
+ container.addMetaDataEntry("type", "user")
+ container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
+
+ metadata_key_to_add = "machine" if is_global_stack else "extruder"
+ container.addMetaDataEntry(metadata_key_to_add, stack_id)
+
+ return container
+
+ @classmethod
+ def createDefinitionChangesContainer(cls, container_stack, container_name):
from cura.CuraApplication import CuraApplication
unique_container_name = ContainerRegistry.getInstance().uniqueName(container_name)
diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py
index f9f0fbb401..2b422ec406 100755
--- a/cura/Settings/ExtruderManager.py
+++ b/cura/Settings/ExtruderManager.py
@@ -12,6 +12,7 @@ from UM.Scene.Selection import Selection
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from UM.Settings.ContainerRegistry import ContainerRegistry # Finding containers by ID.
from UM.Settings.SettingFunction import SettingFunction
+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
@@ -30,28 +31,22 @@ class ExtruderManager(QObject):
def __init__(self, parent = None):
super().__init__(parent)
+ self._application = Application.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._global_container_stack_definition_id = None
self._addCurrentMachineExtruders()
- Application.getInstance().globalContainerStackChanged.connect(self.__globalContainerStackChanged)
+ #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.
extrudersChanged = pyqtSignal(QVariant)
- ## Signal to notify other components when the global container stack is switched to a definition
- # that has different extruders than the previous global container stack
- globalContainerStackDefinitionChanged = pyqtSignal()
-
## Notify when the user switches the currently active extruder.
activeExtruderChanged = pyqtSignal()
- ## The signal notifies subscribers if extruders are added
- extrudersAdded = pyqtSignal()
-
## Gets the unique identifier of the currently active extruder stack.
#
# The currently active extruder stack is the stack that is currently being
@@ -184,6 +179,7 @@ class ExtruderManager(QObject):
self._selected_object_extruders = []
self.selectedObjectExtrudersChanged.emit()
+ @pyqtSlot(result = QObject)
def getActiveExtruderStack(self) -> Optional["ExtruderStack"]:
global_container_stack = Application.getInstance().getGlobalContainerStack()
@@ -245,6 +241,13 @@ class ExtruderManager(QObject):
result.append(extruder_stack.getProperty(setting_key, prop))
return result
+ def extruderValueWithDefault(self, value):
+ machine_manager = self._application.getMachineManager()
+ if value == "-1":
+ return machine_manager.defaultExtruderPosition
+ else:
+ return value
+
## Gets the extruder stacks that are actually being used at the moment.
#
# An extruder stack is being used if it is the extruder to print any mesh
@@ -256,7 +259,7 @@ class ExtruderManager(QObject):
#
# \return A list of extruder stacks.
def getUsedExtruderStacks(self) -> List["ContainerStack"]:
- global_stack = Application.getInstance().getGlobalContainerStack()
+ global_stack = self._application.getGlobalContainerStack()
container_registry = ContainerRegistry.getInstance()
used_extruder_stack_ids = set()
@@ -306,16 +309,19 @@ class ExtruderManager(QObject):
# Check support extruders
if support_enabled:
- used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("support_infill_extruder_nr", "value"))])
- used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("support_extruder_nr_layer_0", "value"))])
+ used_extruder_stack_ids.add(self.extruderIds[self.extruderValueWithDefault(str(global_stack.getProperty("support_infill_extruder_nr", "value")))])
+ used_extruder_stack_ids.add(self.extruderIds[self.extruderValueWithDefault(str(global_stack.getProperty("support_extruder_nr_layer_0", "value")))])
if support_bottom_enabled:
- used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("support_bottom_extruder_nr", "value"))])
+ used_extruder_stack_ids.add(self.extruderIds[self.extruderValueWithDefault(str(global_stack.getProperty("support_bottom_extruder_nr", "value")))])
if support_roof_enabled:
- used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("support_roof_extruder_nr", "value"))])
+ used_extruder_stack_ids.add(self.extruderIds[self.extruderValueWithDefault(str(global_stack.getProperty("support_roof_extruder_nr", "value")))])
# The platform adhesion extruder. Not used if using none.
if global_stack.getProperty("adhesion_type", "value") != "none":
- used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("adhesion_extruder_nr", "value"))])
+ 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])
try:
return [container_registry.findContainerStacks(id = stack_id)[0] for stack_id in used_extruder_stack_ids]
@@ -371,12 +377,7 @@ class ExtruderManager(QObject):
return result[:machine_extruder_count]
- def __globalContainerStackChanged(self) -> None:
- global_container_stack = Application.getInstance().getGlobalContainerStack()
- if global_container_stack and global_container_stack.getBottom() and global_container_stack.getBottom().getId() != self._global_container_stack_definition_id:
- self._global_container_stack_definition_id = global_container_stack.getBottom().getId()
- self.globalContainerStackDefinitionChanged.emit()
-
+ def _globalContainerStackChanged(self) -> None:
# If the global container changed, the machine changed and might have extruders that were not registered yet
self._addCurrentMachineExtruders()
@@ -384,7 +385,7 @@ class ExtruderManager(QObject):
## Adds the extruders of the currently active machine.
def _addCurrentMachineExtruders(self) -> None:
- global_stack = Application.getInstance().getGlobalContainerStack()
+ global_stack = self._application.getGlobalContainerStack()
extruders_changed = False
if global_stack:
@@ -404,20 +405,82 @@ class ExtruderManager(QObject):
self._extruder_trains[global_stack_id][extruder_train.getMetaDataEntry("position")] = extruder_train
# regardless of what the next stack is, we have to set it again, because of signal routing. ???
+ extruder_train.setParent(global_stack)
extruder_train.setNextStack(global_stack)
extruders_changed = True
- # FIX: We have to remove those settings here because we know that those values have been copied to all
- # the extruders at this point.
- for key in ("material_diameter", "machine_nozzle_size"):
- if global_stack.definitionChanges.hasProperty(key, "value"):
- global_stack.definitionChanges.removeInstance(key, postpone_emit = True)
-
+ self._fixMaterialDiameterAndNozzleSize(global_stack, extruder_trains)
if extruders_changed:
self.extrudersChanged.emit(global_stack_id)
- self.extrudersAdded.emit()
self.setActiveExtruderIndex(0)
+ #
+ # This function tries to fix the problem with per-extruder-settable nozzle size and material diameter problems
+ # in early versions (3.0 - 3.2.1).
+ #
+ # In earlier versions, "nozzle size" and "material diameter" are only applicable to the complete machine, so all
+ # extruders share the same values. In this case, "nozzle size" and "material diameter" are saved in the
+ # GlobalStack's DefinitionChanges container.
+ #
+ # Later, we could have different "nozzle size" for each extruder, but "material diameter" could only be set for
+ # the entire machine. In this case, "nozzle size" should be saved in each ExtruderStack's DefinitionChanges, but
+ # "material diameter" still remains in the GlobalStack's DefinitionChanges.
+ #
+ # Lateer, both "nozzle size" and "material diameter" are settable per-extruder, and both settings should be saved
+ # in the ExtruderStack's DefinitionChanges.
+ #
+ # There were some bugs in upgrade so the data weren't saved correct as described above. This function tries fix
+ # this.
+ #
+ # One more thing is about material diameter and single-extrusion machines. Most single-extrusion machines don't
+ # specifically define their extruder definition, so they reuse "fdmextruder", but for those machines, they may
+ # define "material diameter = 1.75" in their machine definition, but in "fdmextruder", it's still "2.85". This
+ # causes a problem with incorrect default values.
+ #
+ # This is also fixed here in this way: If no "material diameter" is specified, it will look for the default value
+ # in both the Extruder's definition and the Global's definition. If 2 values don't match, we will use the value
+ # from the Global definition by setting it in the Extruder's DefinitionChanges container.
+ #
+ def _fixMaterialDiameterAndNozzleSize(self, global_stack, extruder_stack_list):
+ keys_to_copy = ["material_diameter", "machine_nozzle_size"] # these will be copied over to all extruders
+
+ extruder_positions_to_update = set()
+ for extruder_stack in extruder_stack_list:
+ for key in keys_to_copy:
+ # Only copy the value when this extruder doesn't have the value.
+ if extruder_stack.definitionChanges.hasProperty(key, "value"):
+ continue
+
+ setting_value_in_global_def_changes = global_stack.definitionChanges.getProperty(key, "value")
+ setting_value_in_global_def = global_stack.definition.getProperty(key, "value")
+ setting_value = setting_value_in_global_def
+ if setting_value_in_global_def_changes is not None:
+ setting_value = setting_value_in_global_def_changes
+ if setting_value == extruder_stack.definition.getProperty(key, "value"):
+ continue
+
+ setting_definition = global_stack.getSettingDefinition(key)
+ new_instance = SettingInstance(setting_definition, extruder_stack.definitionChanges)
+ new_instance.setProperty("value", setting_value)
+ new_instance.resetState() # Ensure that the state is not seen as a user state.
+ extruder_stack.definitionChanges.addInstance(new_instance)
+ extruder_stack.definitionChanges.setDirty(True)
+
+ # Make sure the material diameter is up to date for the extruder stack.
+ if key == "material_diameter":
+ position = int(extruder_stack.getMetaDataEntry("position"))
+ extruder_positions_to_update.add(position)
+
+ # We have to remove those settings here because we know that those values have been copied to all
+ # the extruders at this point.
+ for key in keys_to_copy:
+ if global_stack.definitionChanges.hasProperty(key, "value"):
+ global_stack.definitionChanges.removeInstance(key, postpone_emit = True)
+
+ # Update material diameter for extruders
+ for position in extruder_positions_to_update:
+ self.updateMaterialForDiameter(position, global_stack = global_stack)
+
## Get all extruder values for a certain setting.
#
# This is exposed to SettingFunction so it can be used in value functions.
@@ -432,6 +495,8 @@ class ExtruderManager(QObject):
result = []
for extruder in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()):
+ if not extruder.isEnabled:
+ continue
# only include values from extruders that are "active" for the current machine instance
if int(extruder.getMetaDataEntry("position")) >= global_stack.getProperty("machine_extruder_count", "value"):
continue
@@ -503,10 +568,11 @@ class ExtruderManager(QObject):
return ExtruderManager.getExtruderValues(key)
## Updates the material container to a material that matches the material diameter set for the printer
- def updateMaterialForDiameter(self, extruder_position: int):
- global_stack = Application.getInstance().getGlobalContainerStack()
+ def updateMaterialForDiameter(self, extruder_position: int, global_stack = None):
if not global_stack:
- return
+ global_stack = Application.getInstance().getGlobalContainerStack()
+ if not global_stack:
+ return
if not global_stack.getMetaDataEntry("has_materials", False):
return
@@ -519,13 +585,19 @@ class ExtruderManager(QObject):
material_diameter = 0
material_approximate_diameter = str(round(material_diameter))
- machine_diameter = extruder_stack.definitionChanges.getProperty("material_diameter", "value")
- if not machine_diameter:
+ material_diameter = extruder_stack.definitionChanges.getProperty("material_diameter", "value")
+ setting_provider = extruder_stack
+ if not material_diameter:
if extruder_stack.definition.hasProperty("material_diameter", "value"):
- machine_diameter = extruder_stack.definition.getProperty("material_diameter", "value")
+ material_diameter = extruder_stack.definition.getProperty("material_diameter", "value")
else:
- machine_diameter = global_stack.definition.getProperty("material_diameter", "value")
- machine_approximate_diameter = str(round(machine_diameter))
+ material_diameter = global_stack.definition.getProperty("material_diameter", "value")
+ setting_provider = global_stack
+
+ if isinstance(material_diameter, SettingFunction):
+ material_diameter = material_diameter(setting_provider)
+
+ machine_approximate_diameter = str(round(material_diameter))
if material_approximate_diameter != machine_approximate_diameter:
Logger.log("i", "The the currently active material(s) do not match the diameter set for the printer. Finding alternatives.")
@@ -596,6 +668,8 @@ class ExtruderManager(QObject):
# global stack if not found.
@staticmethod
def getExtruderValue(extruder_index, key):
+ if extruder_index == -1:
+ extruder_index = int(Application.getInstance().getMachineManager().defaultExtruderPosition)
extruder = ExtruderManager.getInstance().getExtruderStack(extruder_index)
if extruder:
diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py
index 6f89d33393..8dcaaf302e 100644
--- a/cura/Settings/ExtruderStack.py
+++ b/cura/Settings/ExtruderStack.py
@@ -3,16 +3,18 @@
from typing import Any, 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
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.Interfaces import ContainerInterface, PropertyEvaluationContext
-from UM.Settings.SettingInstance import SettingInstance
+from UM.Util import parseBool
from . import Exceptions
-from .CuraContainerStack import CuraContainerStack
+from .CuraContainerStack import CuraContainerStack, _ContainerIndexes
from .ExtruderManager import ExtruderManager
if TYPE_CHECKING:
@@ -30,11 +32,13 @@ class ExtruderStack(CuraContainerStack):
self.propertiesChanged.connect(self._onPropertiesChanged)
+ enabledChanged = pyqtSignal()
+
## Overridden from ContainerStack
#
# This will set the next stack and ensure that we register this stack as an extruder.
@override(ContainerStack)
- def setNextStack(self, stack: ContainerStack) -> None:
+ def setNextStack(self, stack: CuraContainerStack) -> None:
super().setNextStack(stack)
stack.addExtruder(self)
self.addMetaDataEntry("machine", stack.id)
@@ -42,83 +46,47 @@ class ExtruderStack(CuraContainerStack):
# For backward compatibility: Register the extruder with the Extruder Manager
ExtruderManager.getInstance().registerExtruder(self, stack.id)
- # Now each machine will have at least one extruder stack. If this is the first extruder, the extruder-specific
- # settings such as nozzle size and material diameter should be moved from the machine's definition_changes to
- # the this extruder's definition_changes.
- #
- # We do this here because it is tooooo expansive to do it in the version upgrade: During the version upgrade,
- # when we are upgrading a definition_changes container file, there is NO guarantee that other files such as
- # machine an extruder stack files are upgraded before this, so we cannot read those files assuming they are in
- # the latest format.
- #
- # MORE:
- # For single-extrusion machines, nozzle size is saved in the global stack, so the nozzle size value should be
- # carried to the first extruder.
- # For material diameter, it was supposed to be applied to all extruders, so its value should be copied to all
- # extruders.
-
- keys_to_copy = ["material_diameter", "machine_nozzle_size"] # these will be copied over to all extruders
-
- for key in keys_to_copy:
- # Only copy the value when this extruder doesn't have the value.
- if self.definitionChanges.hasProperty(key, "value"):
- continue
-
- # WARNING: this might be very dangerous and should be refactored ASAP!
- #
- # We cannot add a setting definition of "material_diameter" into the extruder's definition at runtime
- # because all other machines which uses "fdmextruder" as the extruder definition will be affected.
- #
- # The problem is that single extrusion machines have their default material diameter defined in the global
- # definitions. Now we automatically create an extruder stack for those machines using "fdmextruder"
- # definition, which doesn't have the specific "material_diameter" and "machine_nozzle_size" defined for
- # each machine. This results in wrong values which can be found in the MachineSettings dialog.
- #
- # To solve this, we put "material_diameter" back into the "fdmextruder" definition because modifying it in
- # the extruder definition will affect all machines which uses the "fdmextruder" definition. Moreover, now
- # we also check the value defined in the machine definition. If present, the value defined in the global
- # stack's definition changes container will be copied. Otherwise, we will check if the default values in the
- # machine definition and the extruder definition are the same, and if not, the default value in the machine
- # definition will be copied to the extruder stack's definition changes.
- #
- setting_value_in_global_def_changes = stack.definitionChanges.getProperty(key, "value")
- setting_value_in_global_def = stack.definition.getProperty(key, "value")
- setting_value = setting_value_in_global_def
- if setting_value_in_global_def_changes is not None:
- setting_value = setting_value_in_global_def_changes
- if setting_value == self.definition.getProperty(key, "value"):
- continue
-
- setting_definition = stack.getSettingDefinition(key)
- new_instance = SettingInstance(setting_definition, self.definitionChanges)
- new_instance.setProperty("value", setting_value)
- new_instance.resetState() # Ensure that the state is not seen as a user state.
- self.definitionChanges.addInstance(new_instance)
- self.definitionChanges.setDirty(True)
-
- # Make sure the material diameter is up to date for the extruder stack.
- if key == "material_diameter":
- from cura.CuraApplication import CuraApplication
- machine_manager = CuraApplication.getInstance().getMachineManager()
- position = self.getMetaDataEntry("position", "0")
- func = lambda p = position: CuraApplication.getInstance().getExtruderManager().updateMaterialForDiameter(p)
- machine_manager.machine_extruder_material_update_dict[stack.getId()].append(func)
-
- # NOTE: We cannot remove the setting from the global stack's definition changes container because for
- # material diameter, it needs to be applied to all extruders, but here we don't know how many extruders
- # a machine actually has and how many extruders has already been loaded for that machine, so we have to
- # keep this setting for any remaining extruders that haven't been loaded yet.
- #
- # Those settings will be removed in ExtruderManager which knows all those info.
-
@override(ContainerStack)
def getNextStack(self) -> Optional["GlobalStack"]:
return super().getNextStack()
+ def setEnabled(self, enabled):
+ 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):
+ return parseBool(self.getMetaDataEntry("enabled", "True"))
+
@classmethod
def getLoadingPriority(cls) -> int:
return 3
+ ## Return the filament diameter that the machine requires.
+ #
+ # If the machine has no requirement for the diameter, -1 is returned.
+ # \return The filament diameter for the printer
+ @property
+ def materialDiameter(self) -> float:
+ context = PropertyEvaluationContext(self)
+ context.context["evaluate_from_container_index"] = _ContainerIndexes.Variant
+
+ return self.getProperty("material_diameter", "value", context = context)
+
+ ## Return the approximate filament diameter that the machine requires.
+ #
+ # The approximate material diameter is the material diameter rounded to
+ # the nearest millimetre.
+ #
+ # If the machine has no requirement for the diameter, -1 is returned.
+ #
+ # \return The approximate filament diameter for the printer
+ @pyqtProperty(float)
+ def approximateMaterialDiameter(self) -> float:
+ return round(float(self.materialDiameter))
+
## Overridden from ContainerStack
#
# It will perform a few extra checks when trying to get properties.
@@ -144,6 +112,8 @@ 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 = 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:
@@ -166,6 +136,8 @@ class ExtruderStack(CuraContainerStack):
@override(CuraContainerStack)
def deserialize(self, contents: str, file_name: Optional[str] = None) -> None:
super().deserialize(contents, file_name)
+ if "enabled" not in self.getMetaData():
+ self.addMetaDataEntry("enabled", "True")
stacks = ContainerRegistry.getInstance().findContainerStacks(id=self.getMetaDataEntry("machine", ""))
if stacks:
self.setNextStack(stacks[0])
@@ -187,11 +159,6 @@ class ExtruderStack(CuraContainerStack):
if has_global_dependencies:
self.getNextStack().propertiesChanged.emit(key, properties)
- def findDefaultVariant(self):
- # The default variant is defined in the machine stack and/or definition, so use the machine stack to find
- # the default variant.
- return self.getNextStack().findDefaultVariant()
-
extruder_stack_mime = MimeType(
name = "application/x-cura-extruderstack",
diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py
index 5139b9885d..f179dabd5a 100644
--- a/cura/Settings/ExtrudersModel.py
+++ b/cura/Settings/ExtrudersModel.py
@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
-from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty, QTimer
+from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot, pyqtProperty, QTimer
from typing import Iterable
from UM.i18n import i18nCatalog
@@ -24,6 +24,8 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
## Human-readable name of the extruder.
NameRole = Qt.UserRole + 2
+ ## Is the extruder enabled?
+ EnabledRole = Qt.UserRole + 9
## Colour of the material loaded in the extruder.
ColorRole = Qt.UserRole + 3
@@ -43,6 +45,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
# The variant of the extruder.
VariantRole = Qt.UserRole + 7
+ StackRole = Qt.UserRole + 8
## List of colours to display if there is no material or the material has no known
# colour.
@@ -57,11 +60,13 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
self.addRoleName(self.IdRole, "id")
self.addRoleName(self.NameRole, "name")
+ self.addRoleName(self.EnabledRole, "enabled")
self.addRoleName(self.ColorRole, "color")
self.addRoleName(self.IndexRole, "index")
self.addRoleName(self.DefinitionRole, "definition")
self.addRoleName(self.MaterialRole, "material")
self.addRoleName(self.VariantRole, "variant")
+ self.addRoleName(self.StackRole, "stack")
self._update_extruder_timer = QTimer()
self._update_extruder_timer.setInterval(100)
@@ -183,11 +188,13 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
item = {
"id": extruder.getId(),
"name": extruder.getName(),
+ "enabled": extruder.isEnabled,
"color": color,
"index": position,
"definition": extruder.getBottom().getId(),
"material": extruder.material.getName() if extruder.material else "",
"variant": extruder.variant.getName() if extruder.variant else "", # e.g. print core
+ "stack": extruder,
}
items.append(item)
@@ -203,6 +210,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
item = {
"id": "",
"name": catalog.i18nc("@menuitem", "Not overridden"),
+ "enabled": True,
"color": "#ffffff",
"index": -1,
"definition": ""
diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py
index 6d18bf615b..5d8a4505a5 100755
--- a/cura/Settings/GlobalStack.py
+++ b/cura/Settings/GlobalStack.py
@@ -7,6 +7,7 @@ from typing import Any, Dict, Optional
from PyQt5.QtCore import pyqtProperty
+from UM.Application import Application
from UM.Decorators import override
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
@@ -104,6 +105,8 @@ class GlobalStack(CuraContainerStack):
# Handle the "limit_to_extruder" property.
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 = 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):
@@ -125,21 +128,6 @@ class GlobalStack(CuraContainerStack):
def setNextStack(self, next_stack: ContainerStack) -> None:
raise Exceptions.InvalidOperationError("Global stack cannot have a next stack!")
- ## Gets the approximate filament diameter that the machine requires.
- #
- # The approximate material diameter is the material diameter rounded to
- # the nearest millimetre.
- #
- # If the machine has no requirement for the diameter, -1 is returned.
- #
- # \return The approximate filament diameter for the printer, as a string.
- @pyqtProperty(str)
- def approximateMaterialDiameter(self) -> str:
- material_diameter = self.definition.getProperty("material_diameter", "value")
- if material_diameter is None:
- return "-1"
- return str(round(float(material_diameter))) #Round, then convert back to string.
-
# protected:
# Determine whether or not we should try to get the "resolve" property instead of the
diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py
index 26b3d3d7ec..c25b58fbcf 100755
--- a/cura/Settings/MachineManager.py
+++ b/cura/Settings/MachineManager.py
@@ -4,31 +4,29 @@
import collections
import time
#Type hinting.
-from typing import Union, List, Dict
+from typing import List, Dict, TYPE_CHECKING, Optional
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Signal import Signal
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QTimer
from UM.FlameProfiler import pyqtSlot
-from PyQt5.QtWidgets import QMessageBox
from UM import Util
from UM.Application import Application
from UM.Preferences import Preferences
from UM.Logger import Logger
from UM.Message import Message
-from UM.Decorators import deprecated
from UM.Settings.ContainerRegistry import ContainerRegistry
-from UM.Settings.ContainerStack import ContainerStack
-from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.SettingFunction import SettingFunction
from UM.Signal import postponeSignals, CompressTechnique
-import UM.FlameProfiler
-from cura.QualityManager import QualityManager
+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.ExtruderManager import ExtruderManager
from .CuraStackBuilder import CuraStackBuilder
@@ -36,50 +34,45 @@ from .CuraStackBuilder import CuraStackBuilder
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
-from cura.Settings.ProfilesModel import ProfilesModel
-from typing import TYPE_CHECKING, Optional
-
if TYPE_CHECKING:
- from UM.Settings.DefinitionContainer import DefinitionContainer
from cura.Settings.CuraContainerStack import CuraContainerStack
from cura.Settings.GlobalStack import GlobalStack
class MachineManager(QObject):
+
def __init__(self, parent = None):
super().__init__(parent)
self._active_container_stack = None # type: CuraContainerStack
self._global_container_stack = None # type: GlobalStack
+ self._current_root_material_id = {}
+ self._current_quality_group = None
+ self._current_quality_changes_group = None
+
+ self._default_extruder_position = "0" # to be updated when extruders are switched on and off
+
self.machine_extruder_material_update_dict = collections.defaultdict(list)
- # Used to store the new containers until after confirming the dialog
- self._new_variant_container = None
- self._new_buildplate_container = None
- self._new_material_container = None
- self._new_quality_containers = []
-
- self._error_check_timer = QTimer()
- self._error_check_timer.setInterval(250)
- self._error_check_timer.setSingleShot(True)
- self._error_check_timer.timeout.connect(self._updateStacksHaveErrors)
-
self._instance_container_timer = QTimer()
self._instance_container_timer.setInterval(250)
self._instance_container_timer.setSingleShot(True)
self._instance_container_timer.timeout.connect(self.__emitChangedSignals)
- Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
- Application.getInstance().getContainerRegistry().containerLoadComplete.connect(self._onInstanceContainersChanged)
- self._connected_to_profiles_model = False
+ self._application = Application.getInstance()
+ self._application.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
+ self._application.getContainerRegistry().containerLoadComplete.connect(self._onInstanceContainersChanged)
## When the global container is changed, active material probably needs to be updated.
self.globalContainerChanged.connect(self.activeMaterialChanged)
self.globalContainerChanged.connect(self.activeVariantChanged)
self.globalContainerChanged.connect(self.activeQualityChanged)
- self._stacks_have_errors = None
+ self.globalContainerChanged.connect(self.activeQualityChangesGroupChanged)
+ self.globalContainerChanged.connect(self.activeQualityGroupChanged)
+
+ self._stacks_have_errors = None # type:Optional[bool]
self._empty_definition_changes_container = ContainerRegistry.getInstance().findContainers(id = "empty_definition_changes")[0]
self._empty_variant_container = ContainerRegistry.getInstance().findContainers(id = "empty_variant")[0]
@@ -101,28 +94,22 @@ class MachineManager(QObject):
ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeStackChanged)
self.activeStackChanged.connect(self.activeStackValueChanged)
- # when a user closed dialog check if any delayed material or variant changes need to be applied
- Application.getInstance().onDiscardOrKeepProfileChangesClosed.connect(self._executeDelayedActiveContainerStackChanges)
-
Preferences.getInstance().addPreference("cura/active_machine", "")
self._global_event_keys = set()
- active_machine_id = Preferences.getInstance().getValue("cura/active_machine")
-
self._printer_output_devices = []
Application.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
# There might already be some output devices by the time the signal is connected
self._onOutputDevicesChanged()
- if active_machine_id != "" and ContainerRegistry.getInstance().findContainerStacksMetadata(id = active_machine_id):
- # An active machine was saved, so restore it.
- self.setActiveMachine(active_machine_id)
- # Make sure _active_container_stack is properly initiated
- ExtruderManager.getInstance().setActiveExtruderIndex(0)
+ self._current_printer_configuration = ConfigurationModel() # Indicates the current configuration setup in this printer
+ self.activeMaterialChanged.connect(self._onCurrentConfigurationChanged)
+ self.activeVariantChanged.connect(self._onCurrentConfigurationChanged)
+ # Force to compute the current configuration
+ self._onCurrentConfigurationChanged()
- self._auto_materials_changed = {}
- self._auto_hotends_changed = {}
+ self._application.callLater(self.setInitialActiveMachine)
self._material_incompatible_message = Message(catalog.i18nc("@info:status",
"The selected material is incompatible with the selected machine or configuration."),
@@ -132,46 +119,89 @@ class MachineManager(QObject):
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()
+
+ # 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.
+ self._material_manager.materialsUpdated.connect(self.rootMaterialChanged)
+ # When the materials get updated, it can be that an activated material's diameter gets changed. In that case,
+ # a material update should be triggered to make sure that the machine still has compatible materials activated.
+ self._material_manager.materialsUpdated.connect(self._updateUponMaterialMetadataChange)
+ self.rootMaterialChanged.connect(self._onRootMaterialChanged)
+
+ activeQualityGroupChanged = pyqtSignal()
+ activeQualityChangesGroupChanged = pyqtSignal()
+
globalContainerChanged = pyqtSignal() # Emitted whenever the global stack is changed (ie: when changing between printers, changing a global profile, but not when changing a value)
activeMaterialChanged = pyqtSignal()
activeVariantChanged = pyqtSignal()
activeQualityChanged = pyqtSignal()
activeStackChanged = pyqtSignal() # Emitted whenever the active stack is changed (ie: when changing between extruders, changing a profile, but not when changing a value)
+ extruderChanged = pyqtSignal()
globalValueChanged = pyqtSignal() # Emitted whenever a value inside global container is changed.
activeStackValueChanged = pyqtSignal() # Emitted whenever a value inside the active stack is changed.
activeStackValidationChanged = pyqtSignal() # Emitted whenever a validation inside active container is changed
stacksValidationChanged = pyqtSignal() # Emitted whenever a validation is changed
+ numberExtrudersEnabledChanged = pyqtSignal() # Emitted when the number of extruders that are enabled changed
blurSettings = pyqtSignal() # Emitted to force fields in the advanced sidebar to un-focus, so they update properly
outputDevicesChanged = pyqtSignal()
+ currentConfigurationChanged = pyqtSignal() # Emitted every time the current configurations of the machine changes
+
+ rootMaterialChanged = pyqtSignal()
+
+ def setInitialActiveMachine(self):
+ active_machine_id = Preferences.getInstance().getValue("cura/active_machine")
+ if active_machine_id != "" and ContainerRegistry.getInstance().findContainerStacksMetadata(id = active_machine_id):
+ # An active machine was saved, so restore it.
+ self.setActiveMachine(active_machine_id)
+ # Make sure _active_container_stack is properly initiated
+ ExtruderManager.getInstance().setActiveExtruderIndex(0)
def _onOutputDevicesChanged(self) -> None:
- for printer_output_device in self._printer_output_devices:
- printer_output_device.hotendIdChanged.disconnect(self._onHotendIdChanged)
- printer_output_device.materialIdChanged.disconnect(self._onMaterialIdChanged)
-
self._printer_output_devices = []
for printer_output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices():
if isinstance(printer_output_device, PrinterOutputDevice):
self._printer_output_devices.append(printer_output_device)
- printer_output_device.hotendIdChanged.connect(self._onHotendIdChanged)
- printer_output_device.materialIdChanged.connect(self._onMaterialIdChanged)
self.outputDevicesChanged.emit()
- @property
- def newVariant(self):
- return self._new_variant_container
+ @pyqtProperty(QObject, notify = currentConfigurationChanged)
+ def currentConfiguration(self):
+ return self._current_printer_configuration
- @property
- def newBuildplate(self):
- return self._new_buildplate_container
+ def _onCurrentConfigurationChanged(self) -> None:
+ if not self._global_container_stack:
+ return
- @property
- def newMaterial(self):
- return self._new_material_container
+ # Create the configuration model with the current data in Cura
+ self._current_printer_configuration.printerType = self._global_container_stack.definition.getName()
+ self._current_printer_configuration.extruderConfigurations = []
+ for extruder in self._global_container_stack.extruders.values():
+ extruder_configuration = ExtruderConfigurationModel()
+ # For compare just the GUID is needed at this moment
+ mat_type = extruder.material.getMetaDataEntry("material") if extruder.material != self._empty_material_container else None
+ mat_guid = extruder.material.getMetaDataEntry("GUID") if extruder.material != self._empty_material_container else None
+ mat_color = extruder.material.getMetaDataEntry("color_name") if extruder.material != self._empty_material_container else None
+ mat_brand = extruder.material.getMetaDataEntry("brand") if extruder.material != self._empty_material_container else None
+ mat_name = extruder.material.getMetaDataEntry("name") if extruder.material != self._empty_material_container else None
+ material_model = MaterialOutputModel(mat_guid, mat_type, mat_color, mat_brand, mat_name)
+
+ extruder_configuration.position = int(extruder.getMetaDataEntry("position"))
+ extruder_configuration.material = material_model
+ extruder_configuration.hotendID = extruder.variant.getName() if extruder.variant != self._empty_variant_container else None
+ self._current_printer_configuration.extruderConfigurations.append(extruder_configuration)
+
+ self._current_printer_configuration.buildplateConfiguration = self._global_container_stack.getProperty("machine_buildplate_type", "value") if self._global_container_stack.variant != self._empty_variant_container else None
+ self.currentConfigurationChanged.emit()
+
+ @pyqtSlot(QObject, result = bool)
+ def matchesConfiguration(self, configuration: ConfigurationModel) -> bool:
+ return self._current_printer_configuration == configuration
@pyqtProperty("QVariantList", notify = outputDevicesChanged)
def printerOutputDevices(self):
@@ -181,115 +211,7 @@ class MachineManager(QObject):
def totalNumberOfSettings(self) -> int:
return len(ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0].getAllKeys())
- def _onHotendIdChanged(self):
- if not self._global_container_stack or not self._printer_output_devices:
- return
-
- active_printer_model = self._printer_output_devices[0].activePrinter
- if not active_printer_model:
- return
-
- change_found = False
- machine_id = self.activeMachineId
- extruders = sorted(ExtruderManager.getInstance().getMachineExtruders(machine_id),
- key=lambda k: k.getMetaDataEntry("position"))
-
- for extruder_model, extruder in zip(active_printer_model.extruders, extruders):
- containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(type="variant",
- definition=self._global_container_stack.definition.getId(),
- name=extruder_model.hotendID)
- if containers:
- # The hotend ID is known.
- machine_id = self.activeMachineId
- if extruder.variant.getName() != extruder_model.hotendID:
- change_found = True
- self._auto_hotends_changed[extruder.getMetaDataEntry("position")] = containers[0]["id"]
-
- if change_found:
- # A change was found, let the output device handle this.
- self._printer_output_devices[0].materialHotendChangedMessage(self._materialHotendChangedCallback)
-
- def _onMaterialIdChanged(self):
- if not self._global_container_stack or not self._printer_output_devices:
- return
-
- active_printer_model = self._printer_output_devices[0].activePrinter
- if not active_printer_model:
- return
-
- change_found = False
- machine_id = self.activeMachineId
- extruders = sorted(ExtruderManager.getInstance().getMachineExtruders(machine_id),
- key=lambda k: k.getMetaDataEntry("position"))
-
- for extruder_model, extruder in zip(active_printer_model.extruders, extruders):
- if extruder_model.activeMaterial is None:
- continue
- containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(type="material",
- definition=self._global_container_stack.definition.getId(),
- GUID=extruder_model.activeMaterial.guid)
- if containers:
- # The material is known.
- if extruder.material.getMetaDataEntry("GUID") != extruder_model.activeMaterial.guid:
- change_found = True
- if self._global_container_stack.definition.getMetaDataEntry("has_variants") and extruder.variant:
- variant_id = self.getQualityVariantId(self._global_container_stack.definition,
- extruder.variant)
- for container in containers:
- if container.get("variant") == variant_id:
- self._auto_materials_changed[extruder.getMetaDataEntry("position")] = container["id"]
- break
- else:
- # Just use the first result we found.
- self._auto_materials_changed[extruder.getMetaDataEntry("position")] = containers[0]["id"]
- if change_found:
- # A change was found, let the output device handle this.
- self._printer_output_devices[0].materialHotendChangedMessage(self._materialHotendChangedCallback)
-
- def _materialHotendChangedCallback(self, button):
- if button == QMessageBox.No:
- self._auto_materials_changed = {}
- self._auto_hotends_changed = {}
- return
- self._autoUpdateMaterials()
- self._autoUpdateHotends()
-
- def _autoUpdateMaterials(self):
- extruder_manager = ExtruderManager.getInstance()
- for position in self._auto_materials_changed:
- material_id = self._auto_materials_changed[position]
- old_index = extruder_manager.activeExtruderIndex
-
- if old_index != int(position):
- extruder_manager.setActiveExtruderIndex(int(position))
- else:
- old_index = None
-
- Logger.log("d", "Setting material of hotend %s to %s" % (position, material_id))
- self.setActiveMaterial(material_id)
-
- if old_index is not None:
- extruder_manager.setActiveExtruderIndex(old_index)
- self._auto_materials_changed = {} #Processed all of them now.
-
- def _autoUpdateHotends(self):
- extruder_manager = ExtruderManager.getInstance()
- for position in self._auto_hotends_changed:
- hotend_id = self._auto_hotends_changed[position]
- old_index = extruder_manager.activeExtruderIndex
-
- if old_index != int(position):
- extruder_manager.setActiveExtruderIndex(int(position))
- else:
- old_index = None
- Logger.log("d", "Setting hotend variant of hotend %s to %s" % (position, hotend_id))
- self.setActiveVariant(hotend_id)
-
- if old_index is not None:
- extruder_manager.setActiveExtruderIndex(old_index)
- self._auto_hotends_changed = {} # Processed all of them now.
-
- def _onGlobalContainerChanged(self):
+ def _onGlobalContainerChanged(self) -> None:
if self._global_container_stack:
try:
self._global_container_stack.nameChanged.disconnect(self._onMachineNameChanged)
@@ -308,9 +230,11 @@ class MachineManager(QObject):
extruder_stack.propertyChanged.disconnect(self._onPropertyChanged)
extruder_stack.containersChanged.disconnect(self._onInstanceContainersChanged)
- # update the local global container stack reference
+ # Update the local global container stack reference
self._global_container_stack = Application.getInstance().getGlobalContainerStack()
-
+ if self._global_container_stack:
+ self.updateDefaultExtruder()
+ self.updateNumberExtrudersEnabled()
self.globalContainerChanged.emit()
# after switching the global stack we reconnect all the signals and set the variant and material references
@@ -342,70 +266,89 @@ class MachineManager(QObject):
Application.getInstance().callLater(func)
del self.machine_extruder_material_update_dict[self._global_container_stack.getId()]
- self._error_check_timer.start()
+ self.activeQualityGroupChanged.emit()
- ## Update self._stacks_valid according to _checkStacksForErrors and emit if change.
- def _updateStacksHaveErrors(self):
- old_stacks_have_errors = self._stacks_have_errors
- self._stacks_have_errors = self._checkStacksHaveErrors()
- if old_stacks_have_errors != self._stacks_have_errors:
- self.stacksValidationChanged.emit()
- Application.getInstance().stacksValidationFinished.emit()
-
- def _onActiveExtruderStackChanged(self):
+ def _onActiveExtruderStackChanged(self) -> None:
self.blurSettings.emit() # Ensure no-one has focus.
old_active_container_stack = self._active_container_stack
self._active_container_stack = ExtruderManager.getInstance().getActiveExtruderStack()
- self._error_check_timer.start()
-
if old_active_container_stack != self._active_container_stack:
# Many methods and properties related to the active quality actually depend
# on _active_container_stack. If it changes, then the properties change.
self.activeQualityChanged.emit()
- def __emitChangedSignals(self):
+ def __emitChangedSignals(self) -> None:
self.activeQualityChanged.emit()
self.activeVariantChanged.emit()
self.activeMaterialChanged.emit()
- self._updateStacksHaveErrors() # Prevents unwanted re-slices after changing machine
- self._error_check_timer.start()
- def _onProfilesModelChanged(self, *args):
- self.__emitChangedSignals()
-
- def _onInstanceContainersChanged(self, container):
- # This should not trigger the ProfilesModel to be created, or there will be an infinite recursion
- if not self._connected_to_profiles_model and ProfilesModel.hasInstance():
- # This triggers updating the qualityModel in SidebarSimple whenever ProfilesModel is updated
- Logger.log("d", "Connecting profiles model...")
- ProfilesModel.getInstance().itemsChanged.connect(self._onProfilesModelChanged)
- self._connected_to_profiles_model = True
+ self.rootMaterialChanged.emit()
+ def _onInstanceContainersChanged(self, container) -> None:
self._instance_container_timer.start()
- def _onPropertyChanged(self, key: str, property_name: str):
+ def _onPropertyChanged(self, key: str, property_name: str) -> None:
if property_name == "value":
# Notify UI items, such as the "changed" star in profile pull down menu.
self.activeStackValueChanged.emit()
- elif property_name == "validationState":
- self._error_check_timer.start()
+ ## Given a global_stack, make sure that it's all valid by searching for this quality group and applying it again
+ def _initMachineState(self, global_stack):
+ material_dict = {}
+ for position, extruder in global_stack.extruders.items():
+ material_dict[position] = extruder.material.getMetaDataEntry("base_file")
+ self._current_root_material_id = material_dict
+ global_quality = global_stack.quality
+ quality_type = global_quality.getMetaDataEntry("quality_type")
+ global_quality_changes = global_stack.qualityChanges
+ global_quality_changes_name = global_quality_changes.getName()
+
+ if global_quality_changes.getId() != "empty_quality_changes":
+ quality_changes_groups = self._application._quality_manager.getQualityChangesGroups(global_stack)
+ if global_quality_changes_name in quality_changes_groups:
+ new_quality_changes_group = quality_changes_groups[global_quality_changes_name]
+ self._setQualityChangesGroup(new_quality_changes_group)
+ else:
+ quality_groups = self._application._quality_manager.getQualityGroups(global_stack)
+ if quality_type not in quality_groups:
+ Logger.log("w", "Quality type [%s] not found in available qualities [%s]", quality_type, str(quality_groups.values()))
+ self._setEmptyQuality()
+ return
+ new_quality_group = quality_groups[quality_type]
+ self._setQualityGroup(new_quality_group, empty_quality_changes = True)
@pyqtSlot(str)
def setActiveMachine(self, stack_id: str) -> None:
self.blurSettings.emit() # Ensure no-one has focus.
- self._cancelDelayedActiveContainerStackChanges()
container_registry = ContainerRegistry.getInstance()
containers = container_registry.findContainerStacks(id = stack_id)
if containers:
- Application.getInstance().setGlobalContainerStack(containers[0])
+ global_stack = containers[0]
+ ExtruderManager.getInstance().setActiveExtruderIndex(0) # Switch to first extruder
+ self._global_container_stack = global_stack
+ Application.getInstance().setGlobalContainerStack(global_stack)
+ ExtruderManager.getInstance()._globalContainerStackChanged()
+ self._initMachineState(containers[0])
+ self._onGlobalContainerChanged()
self.__emitChangedSignals()
+ ## Given a definition id, return the machine with this id.
+ # Optional: add a list of keys and values to filter the list of machines with the given definition id
+ # \param definition_id \type{str} definition id that needs to look for
+ # \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)
+ for machine in machines:
+ if machine.definition.getId() == definition_id:
+ return machine
+ return None
+
@pyqtSlot(str, str)
def addMachine(self, name: str, definition_id: str) -> None:
new_stack = CuraStackBuilder.createMachine(name, definition_id)
@@ -421,7 +364,7 @@ class MachineManager(QObject):
return False
if self._global_container_stack.hasErrors():
- Logger.log("d", "Checking global stack for errors took %0.2f s and we found and error" % (time.time() - time_start))
+ Logger.log("d", "Checking global stack for errors took %0.2f s and we found an error" % (time.time() - time_start))
return True
# Not a very pretty solution, but the extruder manager doesn't really know how many extruders there are
@@ -440,16 +383,6 @@ class MachineManager(QObject):
Logger.log("d", "Checking %s stacks for errors took %.2f s" % (count, time.time() - time_start))
return False
- ## Remove all instances from the top instanceContainer (effectively removing all user-changed settings)
- @pyqtSlot()
- def clearUserSettings(self):
- if not self._active_container_stack:
- return
-
- self.blurSettings.emit()
- user_settings = self._active_container_stack.getTop()
- user_settings.clear()
-
## Check if the global_container has instances in the user container
@pyqtProperty(bool, notify = activeStackValueChanged)
def hasUserSettings(self) -> bool:
@@ -480,7 +413,7 @@ class MachineManager(QObject):
## Delete a user setting from the global stack and all extruder stacks.
# \param key \type{str} the name of the key to delete
@pyqtSlot(str)
- def clearUserSettingAllCurrentStacks(self, key: str):
+ def clearUserSettingAllCurrentStacks(self, key: str) -> None:
if not self._global_container_stack:
return
@@ -515,25 +448,34 @@ class MachineManager(QObject):
def stacksHaveErrors(self) -> bool:
return bool(self._stacks_have_errors)
- @pyqtProperty(str, notify = activeStackChanged)
- def activeUserProfileId(self) -> str:
- if self._active_container_stack:
- return self._active_container_stack.getTop().getId()
-
+ @pyqtProperty(str, notify = globalContainerChanged)
+ def activeMachineDefinitionName(self) -> str:
+ if self._global_container_stack:
+ return self._global_container_stack.definition.getName()
return ""
@pyqtProperty(str, notify = globalContainerChanged)
def activeMachineName(self) -> str:
if self._global_container_stack:
return self._global_container_stack.getName()
-
return ""
@pyqtProperty(str, notify = globalContainerChanged)
def activeMachineId(self) -> str:
if self._global_container_stack:
return self._global_container_stack.getId()
+ return ""
+ @pyqtProperty(str, notify = outputDevicesChanged)
+ def activeMachineNetworkKey(self) -> str:
+ if self._global_container_stack:
+ return self._global_container_stack.getMetaDataEntry("um_network_key", "")
+ return ""
+
+ @pyqtProperty(str, notify = outputDevicesChanged)
+ def activeMachineNetworkGroupName(self) -> str:
+ if self._global_container_stack:
+ return self._global_container_stack.getMetaDataEntry("connect_group_name", "")
return ""
@pyqtProperty(QObject, notify = globalContainerChanged)
@@ -544,42 +486,11 @@ class MachineManager(QObject):
def activeStackId(self) -> str:
if self._active_container_stack:
return self._active_container_stack.getId()
-
return ""
- @pyqtProperty(str, notify = activeMaterialChanged)
- def activeMaterialName(self) -> str:
- if self._active_container_stack:
- material = self._active_container_stack.material
- if material:
- return material.getName()
-
- return ""
-
- @pyqtProperty("QVariantList", notify=activeVariantChanged)
- def activeVariantNames(self) -> List[str]:
- result = []
-
- active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks()
- if active_stacks is not None:
- for stack in active_stacks:
- variant_container = stack.variant
- if variant_container and variant_container != self._empty_variant_container:
- result.append(variant_container.getName())
-
- return result
-
- @pyqtProperty("QVariantList", notify = activeMaterialChanged)
- def activeMaterialNames(self) -> List[str]:
- result = []
-
- active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks()
- if active_stacks is not None:
- for stack in active_stacks:
- material_container = stack.material
- if material_container and material_container != self._empty_material_container:
- result.append(material_container.getName())
- return result
+ @pyqtProperty(QObject, notify = activeStackChanged)
+ def activeStack(self) -> Optional["ExtruderStack"]:
+ return self._active_container_stack
@pyqtProperty(str, notify=activeMaterialChanged)
def activeMaterialId(self) -> str:
@@ -587,24 +498,8 @@ class MachineManager(QObject):
material = self._active_container_stack.material
if material:
return material.getId()
-
return ""
- @pyqtProperty("QVariantMap", notify = activeVariantChanged)
- def allActiveVariantIds(self) -> Dict[str, str]:
- result = {}
-
- active_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
- if active_stacks is not None: #If we have a global stack.
- for stack in active_stacks:
- variant_container = stack.variant
- if not variant_container:
- continue
-
- result[stack.getId()] = variant_container.getId()
-
- return result
-
## Gets a dict with the active materials ids set in all extruder stacks and the global stack
# (when there is one extruder, the material is set in the global stack)
#
@@ -629,106 +524,45 @@ class MachineManager(QObject):
#
# \return The layer height of the currently active quality profile. If
# there is no quality profile, this returns 0.
- @pyqtProperty(float, notify=activeQualityChanged)
+ @pyqtProperty(float, notify = activeQualityGroupChanged)
def activeQualityLayerHeight(self) -> float:
if not self._global_container_stack:
return 0
-
- quality_changes = self._global_container_stack.qualityChanges
- if quality_changes:
- value = self._global_container_stack.getRawProperty("layer_height", "value", skip_until_container = quality_changes.getId())
+ if self._current_quality_changes_group:
+ value = self._global_container_stack.getRawProperty("layer_height", "value", skip_until_container = self._global_container_stack.qualityChanges.getId())
if isinstance(value, SettingFunction):
value = value(self._global_container_stack)
return value
- quality = self._global_container_stack.quality
- if quality:
- value = self._global_container_stack.getRawProperty("layer_height", "value", skip_until_container = quality.getId())
+ elif self._current_quality_group:
+ value = self._global_container_stack.getRawProperty("layer_height", "value", skip_until_container = self._global_container_stack.quality.getId())
if isinstance(value, SettingFunction):
value = value(self._global_container_stack)
return value
+ return 0
- return 0 # No quality profile.
-
- ## Get the Material ID associated with the currently active material
- # \returns MaterialID (string) if found, empty string otherwise
- @pyqtProperty(str, notify=activeQualityChanged)
- def activeQualityMaterialId(self) -> str:
- if self._active_container_stack:
- quality = self._active_container_stack.quality
- if quality:
- material_id = quality.getMetaDataEntry("material")
- if material_id:
- # if the currently active machine inherits its qualities from a different machine
- # definition, make sure to return a material that is relevant to that machine definition
- definition_id = self.activeDefinitionId
- quality_definition_id = self.activeQualityDefinitionId
- if definition_id != quality_definition_id:
- material_id = material_id.replace(definition_id, quality_definition_id, 1)
-
- return material_id
- return ""
-
- @pyqtProperty(str, notify=activeQualityChanged)
- def activeQualityName(self) -> str:
- if self._active_container_stack and self._global_container_stack:
- quality = self._global_container_stack.qualityChanges
- if quality and not isinstance(quality, type(self._empty_quality_changes_container)):
- return quality.getName()
- quality = self._active_container_stack.quality
- if quality:
- return quality.getName()
- return ""
-
- @pyqtProperty(str, notify=activeQualityChanged)
- def activeQualityId(self) -> str:
- if self._active_container_stack:
- quality = self._active_container_stack.quality
- if isinstance(quality, type(self._empty_quality_container)):
- return ""
- quality_changes = self._active_container_stack.qualityChanges
- if quality and quality_changes:
- if isinstance(quality_changes, type(self._empty_quality_changes_container)):
- # It's a built-in profile
- return quality.getId()
- else:
- # Custom profile
- return quality_changes.getId()
- return ""
-
- @pyqtProperty(str, notify=activeQualityChanged)
- def globalQualityId(self) -> str:
- if self._global_container_stack:
- quality = self._global_container_stack.qualityChanges
- if quality and not isinstance(quality, type(self._empty_quality_changes_container)):
- return quality.getId()
- quality = self._global_container_stack.quality
- if quality:
- return quality.getId()
- return ""
-
- @pyqtProperty(str, notify=activeVariantChanged)
- def globalVariantId(self) -> str:
+ @pyqtProperty(str, notify = activeVariantChanged)
+ def globalVariantName(self) -> str:
if self._global_container_stack:
variant = self._global_container_stack.variant
if variant and not isinstance(variant, type(self._empty_variant_container)):
- return variant.getId()
+ return variant.getName()
return ""
- @pyqtProperty(str, notify = activeQualityChanged)
+ @pyqtProperty(str, notify = activeQualityGroupChanged)
def activeQualityType(self) -> str:
+ quality_type = ""
if self._active_container_stack:
- quality = self._active_container_stack.quality
- if quality:
- return quality.getMetaDataEntry("quality_type")
- return ""
+ if self._current_quality_group:
+ quality_type = self._current_quality_group.quality_type
+ return quality_type
- @pyqtProperty(bool, notify = activeQualityChanged)
+ @pyqtProperty(bool, notify = activeQualityGroupChanged)
def isActiveQualitySupported(self) -> bool:
- if self._active_container_stack:
- quality = self._active_container_stack.quality
- if quality:
- return Util.parseBool(quality.getMetaDataEntry("supported", True))
- return False
+ is_supported = False
+ if self._global_container_stack:
+ if self._current_quality_group:
+ is_supported = self._current_quality_group.is_available
+ return is_supported
## Returns whether there is anything unsupported in the current set-up.
#
@@ -747,29 +581,6 @@ class MachineManager(QObject):
return False
return True
- ## Get the Quality ID associated with the currently active extruder
- # Note that this only returns the "quality", not the "quality_changes"
- # \returns QualityID (string) if found, empty string otherwise
- # \sa activeQualityId()
- # \todo Ideally, this method would be named activeQualityId(), and the other one
- # would be named something like activeQualityOrQualityChanges() for consistency
- @pyqtProperty(str, notify = activeQualityChanged)
- def activeQualityContainerId(self) -> str:
- # We're using the active stack instead of the global stack in case the list of qualities differs per extruder
- if self._global_container_stack:
- quality = self._active_container_stack.quality
- if quality:
- return quality.getId()
- return ""
-
- @pyqtProperty(str, notify = activeQualityChanged)
- def activeQualityChangesId(self) -> str:
- if self._active_container_stack:
- quality_changes = self._active_container_stack.qualityChanges
- if quality_changes and not isinstance(quality_changes, type(self._empty_quality_changes_container)):
- return quality_changes.getId()
- return ""
-
## Check if a container is read_only
@pyqtSlot(str, result = bool)
def isReadOnly(self, container_id: str) -> bool:
@@ -786,388 +597,17 @@ class MachineManager(QObject):
if extruder_stack != self._active_container_stack and extruder_stack.getProperty(key, "value") != new_value:
extruder_stack.userChanges.setProperty(key, "value", new_value) # TODO: nested property access, should be improved
- ## Set the active material by switching out a container
- # Depending on from/to material+current variant, a quality profile is chosen and set.
- @pyqtSlot(str)
- def setActiveMaterial(self, material_id: str, always_discard_changes = False):
- with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
- containers = ContainerRegistry.getInstance().findInstanceContainers(id = material_id)
- if not containers or not self._active_container_stack:
- return
- material_container = containers[0]
-
- Logger.log("d", "Attempting to change the active material to %s", material_id)
-
- old_material = self._active_container_stack.material
- old_quality = self._active_container_stack.quality
- old_quality_type = None
- if old_quality and old_quality.getId() != self._empty_quality_container.getId():
- old_quality_type = old_quality.getMetaDataEntry("quality_type")
- old_quality_changes = self._active_container_stack.qualityChanges
- if not old_material:
- Logger.log("w", "While trying to set the active material, no material was found to replace it.")
- return
-
- if old_quality_changes and isinstance(old_quality_changes, type(self._empty_quality_changes_container)):
- old_quality_changes = None
-
- self.blurSettings.emit()
- old_material.nameChanged.disconnect(self._onMaterialNameChanged)
-
- self._new_material_container = material_container # self._active_container_stack will be updated with a delay
- Logger.log("d", "Active material changed")
-
- material_container.nameChanged.connect(self._onMaterialNameChanged)
-
- if material_container.getMetaDataEntry("compatible") == False:
- self._material_incompatible_message.show()
- else:
- self._material_incompatible_message.hide()
-
- quality_type = None
- new_quality_id = None
- if old_quality:
- new_quality_id = old_quality.getId()
- quality_type = old_quality.getMetaDataEntry("quality_type")
- if old_quality_changes:
- quality_type = old_quality_changes.getMetaDataEntry("quality_type")
- new_quality_id = old_quality_changes.getId()
-
- global_stack = Application.getInstance().getGlobalContainerStack()
- if global_stack:
- quality_manager = QualityManager.getInstance()
-
- candidate_quality = None
- if quality_type:
- candidate_quality = quality_manager.findQualityByQualityType(quality_type,
- quality_manager.getWholeMachineDefinition(global_stack.definition),
- [material_container.getMetaData()])
-
- if not candidate_quality or candidate_quality.getId() == self._empty_quality_changes_container:
- Logger.log("d", "Attempting to find fallback quality")
- # Fall back to a quality (which must be compatible with all other extruders)
- new_qualities = quality_manager.findAllUsableQualitiesForMachineAndExtruders(
- self._global_container_stack, ExtruderManager.getInstance().getExtruderStacks())
-
- quality_types = sorted([q.getMetaDataEntry("quality_type") for q in new_qualities], reverse = True)
- quality_type_to_use = None
- if quality_types:
- # try to use the same quality as before, otherwise the first one in the quality_types
- quality_type_to_use = quality_types[0]
- if old_quality_type is not None and old_quality_type in quality_type_to_use:
- quality_type_to_use = old_quality_type
-
- new_quality = None
- for q in new_qualities:
- if quality_type_to_use is not None and q.getMetaDataEntry("quality_type") == quality_type_to_use:
- new_quality = q
- break
-
- if new_quality is not None:
- new_quality_id = new_quality.getId() # Just pick the first available one
- else:
- Logger.log("w", "No quality profile found that matches the current machine and extruders.")
- else:
- if not old_quality_changes:
- new_quality_id = candidate_quality.getId()
-
- self.setActiveQuality(new_quality_id, always_discard_changes = always_discard_changes)
-
- @pyqtSlot(str)
- def setActiveVariant(self, variant_id: str, always_discard_changes = False):
- with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
- containers = ContainerRegistry.getInstance().findInstanceContainers(id = variant_id)
- if not containers or not self._active_container_stack:
- return
- Logger.log("d", "Attempting to change the active variant to %s", variant_id)
- old_variant = self._active_container_stack.variant
- old_material = self._active_container_stack.material
- if old_variant:
- self.blurSettings.emit()
- self._new_variant_container = containers[0] # self._active_container_stack will be updated with a delay
- Logger.log("d", "Active variant changed to {active_variant_id}".format(active_variant_id = containers[0].getId()))
- preferred_material_name = None
- if old_material:
- preferred_material_name = old_material.getName()
- preferred_material_id = self._updateMaterialContainer(self._global_container_stack.definition, self._global_container_stack, containers[0], preferred_material_name).id
- self.setActiveMaterial(preferred_material_id, always_discard_changes = always_discard_changes)
- else:
- Logger.log("w", "While trying to set the active variant, no variant was found to replace.")
-
- @pyqtSlot(str)
- def setActiveVariantBuildplate(self, variant_buildplate_id: str):
- with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
- containers = ContainerRegistry.getInstance().findInstanceContainers(id = variant_buildplate_id)
- if not containers or not self._global_container_stack:
- return
- Logger.log("d", "Attempting to change the active buildplate to %s", variant_buildplate_id)
- old_buildplate = self._global_container_stack.variant
- if old_buildplate:
- self.blurSettings.emit()
- self._new_buildplate_container = containers[0] # self._active_container_stack will be updated with a delay
- Logger.log("d", "Active buildplate changed to {active_variant_buildplate_id}".format(active_variant_buildplate_id = containers[0].getId()))
-
- # Force set the active quality as it is so the values are updated
- self.setActiveMaterial(self._active_container_stack.material.getId())
- else:
- Logger.log("w", "While trying to set the active buildplate, no buildplate was found to replace.")
-
- ## set the active quality
- # \param quality_id The quality_id of either a quality or a quality_changes
- @pyqtSlot(str)
- def setActiveQuality(self, quality_id: str, always_discard_changes = False):
- with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
- self.blurSettings.emit()
-
- Logger.log("d", "Attempting to change the active quality to %s", quality_id)
-
- containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = quality_id)
- if not containers or not self._global_container_stack:
- return
-
- # Quality profile come in two flavours: type=quality and type=quality_changes
- # If we found a quality_changes profile then look up its parent quality profile.
- container_type = containers[0].get("type")
- quality_name = containers[0]["name"]
- quality_type = containers[0].get("quality_type")
-
- # Get quality container and optionally the quality_changes container.
- if container_type == "quality":
- new_quality_settings_list = self.determineQualityAndQualityChangesForQualityType(quality_type)
- elif container_type == "quality_changes":
- new_quality_settings_list = self._determineQualityAndQualityChangesForQualityChanges(quality_name)
- else:
- Logger.log("e", "Tried to set quality to a container that is not of the right type: {container_id}".format(container_id = containers[0]["id"]))
- return
-
- # Check if it was at all possible to find new settings
- if new_quality_settings_list is None:
- return
-
- # check if any of the stacks have a not supported profile
- # if that is the case, all stacks should have a not supported state (otherwise it will show quality_type normal)
- has_not_supported_quality = False
-
- # check all stacks for not supported
- for setting_info in new_quality_settings_list:
- if setting_info["quality"].getMetaDataEntry("quality_type") == "not_supported":
- has_not_supported_quality = True
- break
-
- # set all stacks to not supported if that's the case
- if has_not_supported_quality:
- for setting_info in new_quality_settings_list:
- setting_info["quality"] = self._empty_quality_container
-
- self._new_quality_containers.clear()
-
- # store the upcoming quality profile changes per stack for later execution
- # this prevents re-slicing before the user has made a choice in the discard or keep dialog
- # (see _executeDelayedActiveContainerStackChanges)
- for setting_info in new_quality_settings_list:
- stack = setting_info["stack"]
- stack_quality = setting_info["quality"]
- stack_quality_changes = setting_info["quality_changes"]
-
- self._new_quality_containers.append({
- "stack": stack,
- "quality": stack_quality,
- "quality_changes": stack_quality_changes
- })
-
- Logger.log("d", "Active quality changed")
-
- # show the keep/discard dialog after the containers have been switched. Otherwise, the default values on
- # the dialog will be the those before the switching.
- self._executeDelayedActiveContainerStackChanges()
-
- if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1 and not always_discard_changes:
- Application.getInstance().discardOrKeepProfileChanges()
-
- ## Used to update material and variant in the active container stack with a delay.
- # This delay prevents the stack from triggering a lot of signals (eventually resulting in slicing)
- # before the user decided to keep or discard any of their changes using the dialog.
- # The Application.onDiscardOrKeepProfileChangesClosed signal triggers this method.
- def _executeDelayedActiveContainerStackChanges(self):
-
- Logger.log("d", "Applying configuration changes...")
-
- if self._new_variant_container is not None:
- self._active_container_stack.variant = self._new_variant_container
- self._new_variant_container = None
-
- if self._new_buildplate_container is not None:
- self._global_container_stack.variant = self._new_buildplate_container
- self._new_buildplate_container = None
-
- if self._new_material_container is not None:
- self._active_container_stack.material = self._new_material_container
- self._new_material_container = None
-
- # apply the new quality to all stacks
- if self._new_quality_containers:
- for new_quality in self._new_quality_containers:
- self._replaceQualityOrQualityChangesInStack(new_quality["stack"], new_quality["quality"], postpone_emit = True)
- self._replaceQualityOrQualityChangesInStack(new_quality["stack"], new_quality["quality_changes"], postpone_emit = True)
-
- for new_quality in self._new_quality_containers:
- new_quality["stack"].nameChanged.connect(self._onQualityNameChanged)
- new_quality["stack"].sendPostponedEmits() # Send the signals that were postponed in _replaceQualityOrQualityChangesInStack
-
- self._new_quality_containers.clear()
-
- Logger.log("d", "New configuration applied")
-
- ## Cancel set changes for material and variant in the active container stack.
- # Used for ignoring any changes when switching between printers (setActiveMachine)
- def _cancelDelayedActiveContainerStackChanges(self):
- self._new_material_container = None
- self._new_buildplate_container = None
- self._new_variant_container = None
-
- ## Determine the quality and quality changes settings for the current machine for a quality name.
- #
- # \param quality_name \type{str} the name of the quality.
- # \return \type{List[Dict]} with keys "stack", "quality" and "quality_changes".
- @UM.FlameProfiler.profile
- def determineQualityAndQualityChangesForQualityType(self, quality_type: str) -> List[Dict[str, Union["CuraContainerStack", InstanceContainer]]]:
- quality_manager = QualityManager.getInstance()
- result = []
- empty_quality_changes = self._empty_quality_changes_container
- global_container_stack = self._global_container_stack
- if not global_container_stack:
- return []
-
- global_machine_definition = quality_manager.getParentMachineDefinition(global_container_stack.definition)
- extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
-
- # find qualities for extruders
+ ## Copy the value of all manually changed settings of the current extruder to all other extruders.
+ @pyqtSlot()
+ def copyAllValuesToExtruders(self):
+ extruder_stacks = list(self._global_container_stack.extruders.values())
for extruder_stack in extruder_stacks:
- material_metadata = extruder_stack.material.getMetaData()
+ if extruder_stack != self._active_container_stack:
+ for key in self._active_container_stack.userChanges.getAllKeys():
+ new_value = self._active_container_stack.getProperty(key, "value")
- # TODO: fix this
- if self._new_material_container and extruder_stack.getId() == self._active_container_stack.getId():
- material_metadata = self._new_material_container.getMetaData()
-
- quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material_metadata])
-
- if not quality:
- # No quality profile is found for this quality type.
- quality = self._empty_quality_container
-
- result.append({
- "stack": extruder_stack,
- "quality": quality,
- "quality_changes": empty_quality_changes
- })
-
- # also find a global quality for the machine
- global_quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [], global_quality = "True")
-
- # if there is not global quality but we're using a single extrusion machine, copy the quality of the first extruder - CURA-4482
- if not global_quality and len(extruder_stacks) == 1:
- global_quality = result[0]["quality"]
-
- # if there is still no global quality, set it to empty (not supported)
- if not global_quality:
- global_quality = self._empty_quality_container
-
- result.append({
- "stack": global_container_stack,
- "quality": global_quality,
- "quality_changes": empty_quality_changes
- })
-
- return result
-
- ## Determine the quality and quality changes settings for the current machine for a quality changes name.
- #
- # \param quality_changes_name \type{str} the name of the quality changes.
- # \return \type{List[Dict]} with keys "stack", "quality" and "quality_changes".
- def _determineQualityAndQualityChangesForQualityChanges(self, quality_changes_name: str) -> Optional[List[Dict[str, Union["CuraContainerStack", InstanceContainer]]]]:
- result = []
- quality_manager = QualityManager.getInstance()
-
- global_container_stack = self._global_container_stack
- global_machine_definition = quality_manager.getParentMachineDefinition(global_container_stack.definition)
- quality_changes_profiles = quality_manager.findQualityChangesByName(quality_changes_name, global_machine_definition)
-
- global_quality_changes = [qcp for qcp in quality_changes_profiles if qcp.getMetaDataEntry("extruder") is None]
- if global_quality_changes:
- global_quality_changes = global_quality_changes[0]
- else:
- Logger.log("e", "Could not find the global quality changes container with name %s", quality_changes_name)
- return None
-
- # For the global stack, find a quality which matches the quality_type in
- # the quality changes profile and also satisfies any material constraints.
- quality_type = global_quality_changes.getMetaDataEntry("quality_type")
-
- extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
-
- # append the extruder quality changes
- for extruder_stack in extruder_stacks:
- extruder_definition = quality_manager.getParentMachineDefinition(extruder_stack.definition)
-
- quality_changes_list = [qcp for qcp in quality_changes_profiles if qcp.getMetaDataEntry("extruder") == extruder_definition.getId()]
-
- if quality_changes_list:
- quality_changes = quality_changes_list[0]
- else:
- quality_changes = global_quality_changes
- if not quality_changes:
- quality_changes = self._empty_quality_changes_container
-
- material_metadata = extruder_stack.material.getMetaData()
-
- if self._new_material_container and self._active_container_stack.getId() == extruder_stack.getId():
- material_metadata = self._new_material_container.getMetaData()
-
- quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material_metadata])
-
- if not quality:
- # No quality profile found for this quality type.
- quality = self._empty_quality_container
-
- result.append({
- "stack": extruder_stack,
- "quality": quality,
- "quality_changes": quality_changes
- })
-
- # append the global quality changes
- global_quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, global_quality = "True")
-
- # if there is not global quality but we're using a single extrusion machine, copy the quality of the first extruder - CURA-4482
- if not global_quality and len(extruder_stacks) == 1:
- global_quality = result[0]["quality"]
-
- # if still no global quality changes are found we set it to empty (not supported)
- if not global_quality:
- global_quality = self._empty_quality_container
-
- result.append({
- "stack": global_container_stack,
- "quality": global_quality,
- "quality_changes": global_quality_changes
- })
-
- return result
-
- def _replaceQualityOrQualityChangesInStack(self, stack: "CuraContainerStack", container: "InstanceContainer", postpone_emit = False):
- # Disconnect the signal handling from the old container.
- container_type = container.getMetaDataEntry("type")
- if container_type == "quality":
- stack.quality.nameChanged.disconnect(self._onQualityNameChanged)
- stack.setQuality(container, postpone_emit = postpone_emit)
- stack.quality.nameChanged.connect(self._onQualityNameChanged)
- elif container_type == "quality_changes" or container_type is None:
- # If the container is an empty container, we need to change the quality_changes.
- # Quality can never be set to empty.
- stack.qualityChanges.nameChanged.disconnect(self._onQualityNameChanged)
- stack.setQualityChanges(container, postpone_emit = postpone_emit)
- stack.qualityChanges.nameChanged.connect(self._onQualityNameChanged)
- self._onQualityNameChanged()
+ # check if the value has to be replaced
+ extruder_stack.userChanges.setProperty(key, "value", new_value)
@pyqtProperty(str, notify = activeVariantChanged)
def activeVariantName(self) -> str:
@@ -1178,15 +618,6 @@ class MachineManager(QObject):
return ""
- @pyqtProperty(str, notify = activeVariantChanged)
- def activeVariantId(self) -> str:
- if self._active_container_stack:
- variant = self._active_container_stack.variant
- if variant:
- return variant.getId()
-
- return ""
-
@pyqtProperty(str, notify = activeVariantChanged)
def activeVariantBuildplateName(self) -> str:
if self._global_container_stack:
@@ -1203,55 +634,14 @@ class MachineManager(QObject):
return ""
- @pyqtProperty(str, notify=globalContainerChanged)
- def activeDefinitionName(self) -> str:
- if self._global_container_stack:
- return self._global_container_stack.definition.getName()
-
- return ""
-
## Get the Definition ID to use to select quality profiles for the currently active machine
# \returns DefinitionID (string) if found, empty string otherwise
- # \sa getQualityDefinitionId
@pyqtProperty(str, notify = globalContainerChanged)
def activeQualityDefinitionId(self) -> str:
if self._global_container_stack:
- return self.getQualityDefinitionId(self._global_container_stack.definition)
+ return getMachineDefinitionIDForQualitySearch(self._global_container_stack.definition)
return ""
- ## Get the Definition ID to use to select quality profiles for machines of the specified definition
- # This is normally the id of the definition itself, but machines can specify a different definition to inherit qualities from
- # \param definition (DefinitionContainer) machine definition
- # \returns DefinitionID (string) if found, empty string otherwise
- def getQualityDefinitionId(self, definition: "DefinitionContainer") -> str:
- return QualityManager.getInstance().getParentMachineDefinition(definition).getId()
-
- ## Get the Variant ID to use to select quality profiles for the currently active variant
- # \returns VariantID (string) if found, empty string otherwise
- # \sa getQualityVariantId
- @pyqtProperty(str, notify = activeVariantChanged)
- def activeQualityVariantId(self) -> str:
- if self._active_container_stack:
- variant = self._active_container_stack.variant
- if variant:
- return self.getQualityVariantId(self._global_container_stack.definition, variant)
- return ""
-
- ## Get the Variant ID to use to select quality profiles for variants of the specified definitions
- # This is normally the id of the variant itself, but machines can specify a different definition
- # to inherit qualities from, which has consequences for the variant to use as well
- # \param definition (DefinitionContainer) machine definition
- # \param variant (InstanceContainer) variant definition
- # \returns VariantID (string) if found, empty string otherwise
- def getQualityVariantId(self, definition: "DefinitionContainer", variant: "InstanceContainer") -> str:
- variant_id = variant.getId()
- definition_id = definition.getId()
- quality_definition_id = self.getQualityDefinitionId(definition)
-
- if definition_id != quality_definition_id:
- variant_id = variant_id.replace(definition_id, quality_definition_id, 1)
- return variant_id
-
## Gets how the active definition calls variants
# Caveat: per-definition-variant-title is currently not translated (though the fallback is)
@pyqtProperty(str, notify = globalContainerChanged)
@@ -1283,12 +673,22 @@ class MachineManager(QObject):
if other_machine_stacks:
self.setActiveMachine(other_machine_stacks[0]["id"])
+ metadata = ContainerRegistry.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)
for container in containers:
ContainerRegistry.getInstance().removeContainer(container["id"])
ContainerRegistry.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)
+ if hidden_containers:
+ # This reuses the method and remove all printers recursively
+ self.removeMachine(hidden_containers[0].getId())
+
@pyqtProperty(bool, notify = globalContainerChanged)
def hasMaterials(self) -> bool:
if self._global_container_stack:
@@ -1316,6 +716,8 @@ class MachineManager(QObject):
buildplate_compatible = True # It is compatible by default
extruder_stacks = self._global_container_stack.extruders.values()
for stack in extruder_stacks:
+ if not stack.isEnabled:
+ continue
material_container = stack.material
if material_container == self._empty_material_container:
continue
@@ -1348,22 +750,6 @@ class MachineManager(QObject):
return result
- ## Property to indicate if a machine has "specialized" material profiles.
- # Some machines have their own material profiles that "override" the default catch all profiles.
- @pyqtProperty(bool, notify = globalContainerChanged)
- def filterMaterialsByMachine(self) -> bool:
- if self._global_container_stack:
- return Util.parseBool(self._global_container_stack.getMetaDataEntry("has_machine_materials", False))
- return False
-
- ## Property to indicate if a machine has "specialized" quality profiles.
- # Some machines have their own quality profiles that "override" the default catch all profiles.
- @pyqtProperty(bool, notify = globalContainerChanged)
- def filterQualityByMachine(self) -> bool:
- if self._global_container_stack:
- return Util.parseBool(self._global_container_stack.getMetaDataEntry("has_machine_quality", False))
- return False
-
## 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
@@ -1373,6 +759,43 @@ class MachineManager(QObject):
if containers:
return containers[0].definition.getId()
+ def getIncompatibleSettingsOnEnabledExtruders(self, container):
+ extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
+ result = []
+ for setting_instance in container.findInstances():
+ setting_key = setting_instance.definition.key
+ setting_enabled = self._global_container_stack.getProperty(setting_key, "enabled")
+ if not setting_enabled:
+ # A setting is not visible anymore
+ result.append(setting_key)
+ Logger.log("d", "Reset setting [%s] from [%s] because the setting is no longer enabled", setting_key, container)
+ continue
+
+ if not self._global_container_stack.getProperty(setting_key, "type") in ("extruder", "optional_extruder"):
+ continue
+
+ old_value = container.getProperty(setting_key, "value")
+ if int(old_value) >= extruder_count or not self._global_container_stack.extruders[str(old_value)].isEnabled:
+ result.append(setting_key)
+ Logger.log("d", "Reset setting [%s] in [%s] because its old value [%s] is no longer valid", setting_key, container, old_value)
+ return result
+
+ ## Update extruder number to a valid value when the number of extruders are changed, or when an extruder is changed
+ def correctExtruderSettings(self):
+ 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)
+ for setting_key in add_user_changes:
+ # Apply quality changes that are incompatible to user changes, so we do not change the quality changes itself.
+ self._global_container_stack.userChanges.setProperty(setting_key, "value", self._default_extruder_position)
+ if add_user_changes:
+ caution_message = Message(catalog.i18nc(
+ "@info:generic",
+ "Settings have been changed to match the current availability of extruders: [%s]" % ", ".join(add_user_changes)),
+ lifetime=0,
+ title = catalog.i18nc("@info:title", "Settings updated"))
+ caution_message.show()
+
## 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):
@@ -1386,16 +809,11 @@ class MachineManager(QObject):
if extruder_count == previous_extruder_count:
return
- # reset all extruder number settings whose value is no longer valid
- for setting_instance in self._global_container_stack.userChanges.findInstances():
- setting_key = setting_instance.definition.key
- if not self._global_container_stack.getProperty(setting_key, "type") in ("extruder", "optional_extruder"):
- continue
+ definition_changes_container.setProperty("machine_extruder_count", "value", extruder_count)
- old_value = int(self._global_container_stack.userChanges.getProperty(setting_key, "value"))
- if old_value >= extruder_count:
- self._global_container_stack.userChanges.removeInstance(setting_key)
- Logger.log("d", "Reset [%s] because its old value [%s] is no longer valid ", setting_key, old_value)
+ self.updateDefaultExtruder()
+ self.updateNumberExtrudersEnabled()
+ self.correctExtruderSettings()
# Check to see if any objects are set to print with an extruder that will no longer exist
root_node = Application.getInstance().getController().getScene().getRoot()
@@ -1406,21 +824,19 @@ class MachineManager(QObject):
if extruder_nr is not None and int(extruder_nr) > extruder_count - 1:
node.callDecoration("setActiveExtruder", extruder_manager.getExtruderStack(extruder_count - 1).getId())
- definition_changes_container.setProperty("machine_extruder_count", "value", extruder_count)
-
# Make sure one of the extruder stacks is active
extruder_manager.setActiveExtruderIndex(0)
# Move settable_per_extruder values out of the global container
# After CURA-4482 this should not be the case anymore, but we still want to support older project files.
- global_user_container = self._global_container_stack.getTop()
+ global_user_container = self._global_container_stack.userChanges
# Make sure extruder_stacks exists
extruder_stacks = []
if previous_extruder_count == 1:
extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
- global_user_container = self._global_container_stack.getTop()
+ global_user_container = self._global_container_stack.userChanges
for setting_instance in global_user_container.findInstances():
setting_key = setting_instance.definition.key
@@ -1429,53 +845,76 @@ class MachineManager(QObject):
if settable_per_extruder:
limit_to_extruder = int(self._global_container_stack.getProperty(setting_key, "limit_to_extruder"))
extruder_stack = extruder_stacks[max(0, limit_to_extruder)]
- extruder_stack.getTop().setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value"))
+ extruder_stack.userChanges.setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value"))
global_user_container.removeInstance(setting_key)
# Signal that the global stack has changed
Application.getInstance().globalContainerStackChanged.emit()
+ self.forceUpdateAllSettings()
- @staticmethod
- def createMachineManager():
- return MachineManager()
+ @pyqtSlot(int, result = QObject)
+ def getExtruder(self, position: int):
+ extruder = None
+ if self._global_container_stack:
+ extruder = self._global_container_stack.extruders.get(str(position))
+ return extruder
- @deprecated("Use ExtruderStack.material = ... and it won't be necessary", "2.7")
- def _updateMaterialContainer(self, definition: "DefinitionContainer", stack: "ContainerStack", variant_container: Optional["InstanceContainer"] = None, preferred_material_name: Optional[str] = None) -> InstanceContainer:
- if not definition.getMetaDataEntry("has_materials"):
- return self._empty_material_container
+ def updateDefaultExtruder(self):
+ extruder_items = sorted(self._global_container_stack.extruders.items())
+ old_position = self._default_extruder_position
+ new_default_position = "0"
+ for position, extruder in extruder_items:
+ if extruder.isEnabled:
+ new_default_position = position
+ break
+ if new_default_position != old_position:
+ self._default_extruder_position = new_default_position
+ self.extruderChanged.emit()
- approximate_material_diameter = str(round(stack.getProperty("material_diameter", "value")))
- search_criteria = { "type": "material", "approximate_diameter": approximate_material_diameter }
+ def updateNumberExtrudersEnabled(self):
+ definition_changes_container = self._global_container_stack.definitionChanges
+ machine_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
+ extruder_count = 0
+ for position, extruder in self._global_container_stack.extruders.items():
+ if extruder.isEnabled and int(position) < machine_extruder_count:
+ extruder_count += 1
+ if self.numberExtrudersEnabled != extruder_count:
+ definition_changes_container.setProperty("extruders_enabled_count", "value", extruder_count)
+ self.numberExtrudersEnabledChanged.emit()
- if definition.getMetaDataEntry("has_machine_materials"):
- search_criteria["definition"] = self.getQualityDefinitionId(definition)
+ @pyqtProperty(int, notify = numberExtrudersEnabledChanged)
+ def numberExtrudersEnabled(self):
+ return self._global_container_stack.definitionChanges.getProperty("extruders_enabled_count", "value")
- if definition.getMetaDataEntry("has_variants") and variant_container:
- search_criteria["variant"] = self.getQualityVariantId(definition, variant_container)
- else:
- search_criteria["definition"] = "fdmprinter"
+ @pyqtProperty(str, notify = extruderChanged)
+ def defaultExtruderPosition(self):
+ return self._default_extruder_position
- if preferred_material_name:
- search_criteria["name"] = preferred_material_name
- else:
- preferred_material = definition.getMetaDataEntry("preferred_material")
- if preferred_material:
- search_criteria["id"] = preferred_material
+ ## This will fire the propertiesChanged for all settings so they will be updated in the front-end
+ @pyqtSlot()
+ def forceUpdateAllSettings(self):
+ 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()):
+ for setting_key in container.getAllKeys():
+ container.propertiesChanged.emit(setting_key, property_names)
- containers = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
- if containers:
- return containers[0]
-
- if "variant" in search_criteria or "id" in search_criteria:
- # If a material by this name can not be found, try a wider set of search criteria
- search_criteria.pop("variant", None)
- search_criteria.pop("id", None)
-
- containers = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
- if containers:
- return containers[0]
- Logger.log("w", "Unable to find a material container with provided criteria, returning an empty one instead.")
- return self._empty_material_container
+ @pyqtSlot(int, bool)
+ def setExtruderEnabled(self, position: int, enabled) -> None:
+ extruder = self.getExtruder(position)
+ extruder.setEnabled(enabled)
+ self.updateDefaultExtruder()
+ self.updateNumberExtrudersEnabled()
+ self.correctExtruderSettings()
+ # ensure that the quality profile is compatible with current combination, or choose a compatible one if available
+ self._updateQualityWithMaterial()
+ self.extruderChanged.emit()
+ # update material compatibility color
+ self.activeQualityGroupChanged.emit()
+ # update items in SettingExtruder
+ ExtruderManager.getInstance().extrudersChanged.emit(self._global_container_stack.getId())
+ # Make sure the front end reflects changes
+ self.forceUpdateAllSettings()
def _onMachineNameChanged(self):
self.globalContainerChanged.emit()
@@ -1490,3 +929,393 @@ class MachineManager(QObject):
stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
stacks.append(self._global_container_stack)
return [ s.containersChanged for s in stacks ]
+
+ @pyqtSlot(str, str, str)
+ def setSettingForAllExtruders(self, setting_name: str, property_name: str, property_value: str):
+ for key, extruder in self._global_container_stack.extruders.items():
+ container = extruder.userChanges
+ container.setProperty(setting_name, property_name, property_value)
+
+ @pyqtProperty("QVariantList", notify = globalContainerChanged)
+ def currentExtruderPositions(self):
+ if self._global_container_stack is None:
+ return []
+ return sorted(list(self._global_container_stack.extruders.keys()))
+
+ ## Update _current_root_material_id when the current root material was changed.
+ def _onRootMaterialChanged(self):
+ self._current_root_material_id = {}
+
+ if self._global_container_stack:
+ for position in self._global_container_stack.extruders:
+ self._current_root_material_id[position] = self._global_container_stack.extruders[position].material.getMetaDataEntry("base_file")
+
+ @pyqtProperty("QVariant", notify = rootMaterialChanged)
+ def currentRootMaterialId(self):
+ return self._current_root_material_id
+
+ ## Return the variant names in the extruder stack(s).
+ ## For the variant in the global stack, use activeVariantBuildplateName
+ @pyqtProperty("QVariant", notify = activeVariantChanged)
+ def activeVariantNames(self):
+ result = {}
+
+ active_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
+ if active_stacks is not None:
+ for stack in active_stacks:
+ variant_container = stack.variant
+ position = stack.getMetaDataEntry("position")
+ if variant_container and variant_container != self._empty_variant_container:
+ result[position] = variant_container.getName()
+
+ return result
+
+ #
+ # Sets all quality and quality_changes containers to empty_quality and empty_quality_changes containers
+ # for all stacks in the currently active machine.
+ #
+ def _setEmptyQuality(self):
+ self._current_quality_group = None
+ self._current_quality_changes_group = None
+ self._global_container_stack.quality = self._empty_quality_container
+ self._global_container_stack.qualityChanges = self._empty_quality_changes_container
+ for extruder in self._global_container_stack.extruders.values():
+ extruder.quality = self._empty_quality_container
+ extruder.qualityChanges = self._empty_quality_changes_container
+
+ self.activeQualityGroupChanged.emit()
+ self.activeQualityChangesGroupChanged.emit()
+
+ def _setQualityGroup(self, quality_group, empty_quality_changes = True):
+ self._current_quality_group = quality_group
+ if empty_quality_changes:
+ self._current_quality_changes_group = None
+
+ # Set quality and quality_changes for the GlobalStack
+ self._global_container_stack.quality = quality_group.node_for_global.getContainer()
+ if empty_quality_changes:
+ self._global_container_stack.qualityChanges = self._empty_quality_changes_container
+
+ # Set quality and quality_changes for each ExtruderStack
+ for position, node in quality_group.nodes_for_extruders.items():
+ self._global_container_stack.extruders[str(position)].quality = node.getContainer()
+ if empty_quality_changes:
+ self._global_container_stack.extruders[str(position)].qualityChanges = self._empty_quality_changes_container
+
+ self.activeQualityGroupChanged.emit()
+ self.activeQualityChangesGroupChanged.emit()
+
+ def _setQualityChangesGroup(self, quality_changes_group):
+ quality_type = quality_changes_group.quality_type
+ quality_group_dict = self._quality_manager.getQualityGroups(self._global_container_stack)
+ quality_group = quality_group_dict[quality_type]
+
+ quality_changes_container = self._empty_quality_changes_container
+ quality_container = self._empty_quality_changes_container
+ if quality_changes_group.node_for_global:
+ quality_changes_container = quality_changes_group.node_for_global.getContainer()
+ if quality_group.node_for_global:
+ quality_container = quality_group.node_for_global.getContainer()
+
+ self._global_container_stack.quality = quality_container
+ self._global_container_stack.qualityChanges = quality_changes_container
+
+ for position, extruder in self._global_container_stack.extruders.items():
+ quality_changes_node = quality_changes_group.nodes_for_extruders.get(position)
+ quality_node = quality_group.nodes_for_extruders.get(position)
+
+ quality_changes_container = self._empty_quality_changes_container
+ quality_container = self._empty_quality_container
+ if quality_changes_node:
+ quality_changes_container = quality_changes_node.getContainer()
+ if quality_node:
+ quality_container = quality_node.getContainer()
+
+ extruder.quality = quality_container
+ extruder.qualityChanges = quality_changes_container
+
+ self._current_quality_group = quality_group
+ self._current_quality_changes_group = quality_changes_group
+ self.activeQualityGroupChanged.emit()
+ self.activeQualityChangesGroupChanged.emit()
+
+ def _setVariantNode(self, position, container_node):
+ self._global_container_stack.extruders[position].variant = container_node.getContainer()
+ self.activeVariantChanged.emit()
+
+ def _setGlobalVariant(self, container_node):
+ self._global_container_stack.variant = container_node.getContainer()
+
+ def _setMaterial(self, position, container_node = None):
+ if container_node:
+ self._global_container_stack.extruders[position].material = container_node.getContainer()
+ root_material_id = container_node.metadata["base_file"]
+ else:
+ self._global_container_stack.extruders[position].material = self._empty_material_container
+ root_material_id = None
+ # The _current_root_material_id is used in the MaterialMenu to see which material is selected
+ if root_material_id != self._current_root_material_id[position]:
+ self._current_root_material_id[position] = root_material_id
+ self.rootMaterialChanged.emit()
+
+ def activeMaterialsCompatible(self):
+ # 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
+ return True
+
+ ## Update current quality type and machine after setting material
+ def _updateQualityWithMaterial(self, *args):
+ Logger.log("i", "Updating quality/quality_changes due to material change")
+ current_quality_type = None
+ if self._current_quality_group:
+ current_quality_type = self._current_quality_group.quality_type
+ candidate_quality_groups = self._quality_manager.getQualityGroups(self._global_container_stack)
+ available_quality_types = {qt for qt, g in candidate_quality_groups.items() if g.is_available}
+
+ Logger.log("d", "Current quality type = [%s]", current_quality_type)
+ if not self.activeMaterialsCompatible():
+ Logger.log("i", "Active materials are not compatible, setting all qualities to empty (Not Supported).")
+ self._setEmptyQuality()
+ return
+
+ if not available_quality_types:
+ Logger.log("i", "No available quality types found, setting all qualities to empty (Not Supported).")
+ self._setEmptyQuality()
+ return
+
+ if current_quality_type in available_quality_types:
+ Logger.log("i", "Current available quality type [%s] is available, applying changes.", current_quality_type)
+ self._setQualityGroup(candidate_quality_groups[current_quality_type], empty_quality_changes = False)
+ return
+
+ # The current quality type is not available so we use the preferred quality type if it's available,
+ # otherwise use one of the available quality types.
+ quality_type = sorted(list(available_quality_types))[0]
+ preferred_quality_type = self._global_container_stack.getMetaDataEntry("preferred_quality_type")
+ if preferred_quality_type in available_quality_types:
+ quality_type = preferred_quality_type
+
+ Logger.log("i", "The current quality type [%s] is not available, switching to [%s] instead",
+ current_quality_type, quality_type)
+ self._setQualityGroup(candidate_quality_groups[quality_type], empty_quality_changes = True)
+
+ def _updateMaterialWithVariant(self, position: Optional[str]):
+ if position is None:
+ position_list = list(self._global_container_stack.extruders.keys())
+ else:
+ position_list = [position]
+
+ for position in position_list:
+ extruder = self._global_container_stack.extruders[position]
+
+ current_material_base_name = extruder.material.getMetaDataEntry("base_file")
+ current_variant_name = None
+ if extruder.variant.getId() != self._empty_variant_container.getId():
+ current_variant_name = extruder.variant.getMetaDataEntry("name")
+
+ from UM.Settings.Interfaces import PropertyEvaluationContext
+ from cura.Settings.CuraContainerStack import _ContainerIndexes
+ context = PropertyEvaluationContext(extruder)
+ context.context["evaluate_from_container_index"] = _ContainerIndexes.DefinitionChanges
+ material_diameter = extruder.getProperty("material_diameter", "value", context)
+ candidate_materials = self._material_manager.getAvailableMaterials(
+ self._global_container_stack.definition.getId(),
+ current_variant_name,
+ material_diameter)
+
+ if not candidate_materials:
+ self._setMaterial(position, 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)
+ 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)
+
+ ## 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):
+ # Don't switch if the user tries to change to the same type of printer
+ if 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()
+ # 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
+ if not new_machine:
+ new_machine = CuraStackBuilder.createMachine(machine_definition_id + "_sync", machine_definition_id)
+ new_machine.addMetaDataEntry("um_network_key", self.activeMachineNetworkKey)
+ new_machine.addMetaDataEntry("connect_group_name", self.activeMachineNetworkGroupName)
+ new_machine.addMetaDataEntry("hidden", False)
+ else:
+ Logger.log("i", "Found a %s with the key %s. Let's use it!", machine_name, self.activeMachineNetworkKey)
+ new_machine.setMetaDataEntry("hidden", False)
+
+ # Set the current printer instance to hidden (the metadata entry must exist)
+ self._global_container_stack.setMetaDataEntry("hidden", True)
+
+ self.setActiveMachine(new_machine.getId())
+
+ @pyqtSlot(QObject)
+ def applyRemoteConfiguration(self, configuration: ConfigurationModel):
+ self.blurSettings.emit()
+ with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
+ self.switchPrinterType(configuration.printerType)
+ for extruder_configuration in configuration.extruderConfigurations:
+ position = str(extruder_configuration.position)
+ variant_container_node = self._variant_manager.getVariantNode(self._global_container_stack.definition.getId(), extruder_configuration.hotendID)
+ material_container_node = self._material_manager.getMaterialNodeByType(self._global_container_stack, extruder_configuration.hotendID,extruder_configuration.material.guid)
+ if variant_container_node:
+ self._setVariantNode(position, variant_container_node)
+ else:
+ self._global_container_stack.extruders[position].variant = self._empty_variant_container
+
+ if material_container_node:
+ self._setMaterial(position, material_container_node)
+ else:
+ self._global_container_stack.extruders[position].material = self._empty_material_container
+ self._updateMaterialWithVariant(position)
+
+ if configuration.buildplateConfiguration is not None:
+ global_variant_container_node = self._variant_manager.getBuildplateVariantNode(self._global_container_stack.definition.getId(), configuration.buildplateConfiguration)
+ if global_variant_container_node:
+ self._setGlobalVariant(global_variant_container_node)
+ else:
+ self._global_container_stack.variant = self._empty_variant_container
+ else:
+ self._global_container_stack.variant = self._empty_variant_container
+ self._updateQualityWithMaterial()
+
+ ## 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):
+ machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine")
+ for machine in machines:
+ if machine.getMetaDataEntry(key) == value:
+ machine.setMetaDataEntry(key, new_value)
+
+ ## This method checks if the name of the group stored in the definition container is correct.
+ # After updating from 3.2 to 3.3 some group names may be temporary. If there is a mismatch in the name of the group
+ # then all the container stacks are updated, both the current and the hidden ones.
+ def checkCorrectGroupName(self, device_id: str, group_name: str):
+ if self._global_container_stack and device_id == self.activeMachineNetworkKey:
+ # 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}
+ hidden_containers = ContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
+ for container in hidden_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)
+ return bool(containers)
+
+ @pyqtSlot("QVariant")
+ def setGlobalVariant(self, container_node):
+ self.blurSettings.emit()
+ with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
+ self._setGlobalVariant(container_node)
+ self._updateMaterialWithVariant(None) # Update all materials
+ self._updateQualityWithMaterial()
+
+ @pyqtSlot(str, str)
+ def setMaterialById(self, position, root_material_id):
+ machine_definition_id = self._global_container_stack.definition.id
+ position = str(position)
+ extruder_stack = self._global_container_stack.extruders[position]
+ variant_name = extruder_stack.variant.getName()
+ material_diameter = extruder_stack.approximateMaterialDiameter
+ material_node = self._material_manager.getMaterialNode(machine_definition_id, variant_name, material_diameter, root_material_id)
+ self.setMaterial(position, material_node)
+
+ @pyqtSlot(str, "QVariant")
+ def setMaterial(self, position, container_node):
+ position = str(position)
+ self.blurSettings.emit()
+ with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
+ self._setMaterial(position, container_node)
+ self._updateQualityWithMaterial()
+
+ @pyqtSlot(str, str)
+ def setVariantByName(self, position, variant_name):
+ 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, container_node):
+ position = str(position)
+ self.blurSettings.emit()
+ with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
+ self._setVariantNode(position, container_node)
+ self._updateMaterialWithVariant(position)
+ self._updateQualityWithMaterial()
+
+ @pyqtSlot(str)
+ def setQualityGroupByQualityType(self, quality_type):
+ # Get all the quality groups for this global stack and filter out by quality_type
+ quality_group_dict = self._quality_manager.getQualityGroups(self._global_container_stack)
+ quality_group = quality_group_dict[quality_type]
+ self.setQualityGroup(quality_group)
+
+ @pyqtSlot(QObject)
+ def setQualityGroup(self, quality_group, no_dialog = False):
+ self.blurSettings.emit()
+ with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
+ self._setQualityGroup(quality_group)
+
+ # See if we need to show the Discard or Keep changes screen
+ if not no_dialog and self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1:
+ self._application.discardOrKeepProfileChanges()
+
+ @pyqtProperty(QObject, fset = setQualityGroup, notify = activeQualityGroupChanged)
+ def activeQualityGroup(self):
+ return self._current_quality_group
+
+ @pyqtSlot(QObject)
+ def setQualityChangesGroup(self, quality_changes_group, no_dialog = False):
+ self.blurSettings.emit()
+ with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
+ self._setQualityChangesGroup(quality_changes_group)
+
+ # See if we need to show the Discard or Keep changes screen
+ if not no_dialog and self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1:
+ self._application.discardOrKeepProfileChanges()
+
+ @pyqtSlot()
+ def resetToUseDefaultQuality(self):
+ 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):
+ return self._current_quality_changes_group
+
+ @pyqtProperty(str, notify = activeQualityGroupChanged)
+ def activeQualityOrQualityChangesName(self):
+ name = self._empty_quality_container.getName()
+ if self._current_quality_changes_group:
+ name = self._current_quality_changes_group.name
+ elif self._current_quality_group:
+ name = self._current_quality_group.name
+ return name
+
+ def _updateUponMaterialMetadataChange(self):
+ with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
+ self._updateMaterialWithVariant(None)
+ self._updateQualityWithMaterial()
diff --git a/cura/Settings/MaterialManager.py b/cura/Settings/MaterialManager.py
deleted file mode 100644
index 80d2723438..0000000000
--- a/cura/Settings/MaterialManager.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright (c) 2017 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-from PyQt5.QtCore import QObject, pyqtSlot #To expose data to QML.
-
-from cura.Settings.ContainerManager import ContainerManager
-from UM.Logger import Logger
-from UM.Message import Message #To create a warning message about material diameter.
-from UM.i18n import i18nCatalog #Translated strings.
-
-catalog = i18nCatalog("cura")
-
-## Handles material-related data, processing requests to change them and
-# providing data for the GUI.
-#
-# TODO: Move material-related managing over from the machine manager to here.
-class MaterialManager(QObject):
- ## Creates the global values for the material manager to use.
- def __init__(self, parent = None):
- super().__init__(parent)
-
- #Material diameter changed warning message.
- self._material_diameter_warning_message = Message(catalog.i18nc("@info:status Has a cancel button next to it.",
- "The selected material diameter causes the material to become incompatible with the current printer."), title = catalog.i18nc("@info:title", "Incompatible Material"))
- self._material_diameter_warning_message.addAction("Undo", catalog.i18nc("@action:button", "Undo"), None, catalog.i18nc("@action", "Undo changing the material diameter."))
- self._material_diameter_warning_message.actionTriggered.connect(self._materialWarningMessageAction)
-
- ## Creates an instance of the MaterialManager.
- #
- # This should only be called by PyQt to create the singleton instance of
- # this class.
- @staticmethod
- def createMaterialManager(engine = None, script_engine = None):
- return MaterialManager()
-
- @pyqtSlot(str, str)
- def showMaterialWarningMessage(self, material_id, previous_diameter):
- self._material_diameter_warning_message.previous_diameter = previous_diameter #Make sure that the undo button can properly undo the action.
- self._material_diameter_warning_message.material_id = material_id
- self._material_diameter_warning_message.show()
-
- ## Called when clicking "undo" on the warning dialogue for disappeared
- # materials.
- #
- # This executes the undo action, restoring the material diameter.
- #
- # \param button The identifier of the button that was pressed.
- def _materialWarningMessageAction(self, message, button):
- if button == "Undo":
- container_manager = ContainerManager.getInstance()
- container_manager.setContainerMetaDataEntry(self._material_diameter_warning_message.material_id, "properties/diameter", self._material_diameter_warning_message.previous_diameter)
- approximate_previous_diameter = str(round(float(self._material_diameter_warning_message.previous_diameter)))
- container_manager.setContainerMetaDataEntry(self._material_diameter_warning_message.material_id, "approximate_diameter", approximate_previous_diameter)
- container_manager.setContainerProperty(self._material_diameter_warning_message.material_id, "material_diameter", "value", self._material_diameter_warning_message.previous_diameter);
- message.hide()
- else:
- Logger.log("w", "Unknown button action for material diameter warning message: {action}".format(action = button))
\ No newline at end of file
diff --git a/cura/Settings/MaterialSettingsVisibilityHandler.py b/cura/Settings/MaterialSettingsVisibilityHandler.py
index 5b6050d2c0..ce545f4551 100644
--- a/cura/Settings/MaterialSettingsVisibilityHandler.py
+++ b/cura/Settings/MaterialSettingsVisibilityHandler.py
@@ -3,13 +3,14 @@
import UM.Settings.Models.SettingVisibilityHandler
+
class MaterialSettingsVisibilityHandler(UM.Settings.Models.SettingVisibilityHandler.SettingVisibilityHandler):
def __init__(self, parent = None, *args, **kwargs):
super().__init__(parent = parent, *args, **kwargs)
material_settings = {
"default_material_print_temperature",
- "material_bed_temperature",
+ "default_material_bed_temperature",
"material_standby_temperature",
#"material_flow_temp_graph",
"cool_fan_speed",
diff --git a/cura/Settings/MaterialsModel.py b/cura/Settings/MaterialsModel.py
deleted file mode 100644
index c4b0329336..0000000000
--- a/cura/Settings/MaterialsModel.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright (c) 2017 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-from typing import Any, List
-from UM.Settings.ContainerRegistry import ContainerRegistry #To listen for changes to the materials.
-from UM.Settings.Models.InstanceContainersModel import InstanceContainersModel #We're extending this class.
-
-## A model that shows a list of currently valid materials.
-class MaterialsModel(InstanceContainersModel):
- def __init__(self, parent = None):
- super().__init__(parent)
-
- ContainerRegistry.getInstance().containerMetaDataChanged.connect(self._onContainerMetaDataChanged)
-
- ## Called when the metadata of the container was changed.
- #
- # This makes sure that we only update when it was a material that changed.
- #
- # \param container The container whose metadata was changed.
- def _onContainerMetaDataChanged(self, container):
- if container.getMetaDataEntry("type") == "material": #Only need to update if a material was changed.
- self._container_change_timer.start()
-
- def _onContainerChanged(self, container):
- if container.getMetaDataEntry("type", "") == "material":
- super()._onContainerChanged(container)
-
- ## Group brand together
- def _sortKey(self, item) -> List[Any]:
- result = []
- result.append(item["metadata"]["brand"])
- result.append(item["metadata"]["material"])
- result.append(item["metadata"]["name"])
- result.append(item["metadata"]["color_name"])
- result.append(item["metadata"]["id"])
- result.extend(super()._sortKey(item))
- return result
diff --git a/cura/Settings/PerObjectContainerStack.py b/cura/Settings/PerObjectContainerStack.py
index 6c54ed46d5..33111cbed7 100644
--- a/cura/Settings/PerObjectContainerStack.py
+++ b/cura/Settings/PerObjectContainerStack.py
@@ -3,13 +3,14 @@ from typing import Any, Optional
from UM.Application import Application
from UM.Decorators import override
from UM.Settings.Interfaces import PropertyEvaluationContext
-from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.SettingInstance import InstanceState
+from .CuraContainerStack import CuraContainerStack
-class PerObjectContainerStack(ContainerStack):
- @override(ContainerStack)
+class PerObjectContainerStack(CuraContainerStack):
+
+ @override(CuraContainerStack)
def getProperty(self, key: str, property_name: str, context: Optional[PropertyEvaluationContext] = None) -> Any:
if context is None:
context = PropertyEvaluationContext()
@@ -51,8 +52,8 @@ class PerObjectContainerStack(ContainerStack):
context.popContainer()
return result
- @override(ContainerStack)
- def setNextStack(self, stack: ContainerStack):
+ @override(CuraContainerStack)
+ def setNextStack(self, stack: CuraContainerStack):
super().setNextStack(stack)
# trigger signal to re-evaluate all default settings
diff --git a/cura/Settings/ProfilesModel.py b/cura/Settings/ProfilesModel.py
deleted file mode 100644
index 77cd407457..0000000000
--- a/cura/Settings/ProfilesModel.py
+++ /dev/null
@@ -1,222 +0,0 @@
-# Copyright (c) 2017 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-from collections import OrderedDict
-
-from PyQt5.QtCore import Qt
-
-from UM.Application import Application
-from UM.Settings.ContainerRegistry import ContainerRegistry
-from UM.Settings.Models.InstanceContainersModel import InstanceContainersModel
-
-from cura.QualityManager import QualityManager
-from cura.Settings.ExtruderManager import ExtruderManager
-
-from typing import List, TYPE_CHECKING
-
-if TYPE_CHECKING:
- from cura.Settings.ExtruderStack import ExtruderStack
-
-
-## QML Model for listing the current list of valid quality profiles.
-#
-class ProfilesModel(InstanceContainersModel):
- LayerHeightRole = Qt.UserRole + 1001
- LayerHeightWithoutUnitRole = Qt.UserRole + 1002
- AvailableRole = Qt.UserRole + 1003
-
- def __init__(self, parent = None):
- super().__init__(parent)
- self.addRoleName(self.LayerHeightRole, "layer_height")
- self.addRoleName(self.LayerHeightWithoutUnitRole, "layer_height_without_unit")
- self.addRoleName(self.AvailableRole, "available")
-
- Application.getInstance().globalContainerStackChanged.connect(self._update)
- Application.getInstance().getMachineManager().activeVariantChanged.connect(self._update)
- Application.getInstance().getMachineManager().activeStackChanged.connect(self._update)
- Application.getInstance().getMachineManager().activeMaterialChanged.connect(self._update)
-
- self._empty_quality = ContainerRegistry.getInstance().findContainers(id = "empty_quality")[0]
-
- # Factory function, used by QML
- @staticmethod
- def createProfilesModel(engine, js_engine):
- return ProfilesModel.getInstance()
-
- ## Get the singleton instance for this class.
- @classmethod
- def getInstance(cls) -> "ProfilesModel":
- # Note: Explicit use of class name to prevent issues with inheritance.
- if not ProfilesModel.__instance:
- ProfilesModel.__instance = cls()
- return ProfilesModel.__instance
-
- @classmethod
- def hasInstance(cls) -> bool:
- return ProfilesModel.__instance is not None
-
- __instance = None # type: "ProfilesModel"
-
- ## Fetch the list of containers to display.
- #
- # See UM.Settings.Models.InstanceContainersModel._fetchInstanceContainers().
- def _fetchInstanceContainers(self):
- global_container_stack = Application.getInstance().getGlobalContainerStack()
- if global_container_stack is None:
- return {}, {}
- global_stack_definition = global_container_stack.definition
-
- # Get the list of extruders and place the selected extruder at the front of the list.
- extruder_stacks = self._getOrderedExtruderStacksList()
- materials = [extruder.material for extruder in extruder_stacks]
-
- # Fetch the list of usable qualities across all extruders.
- # The actual list of quality profiles come from the first extruder in the extruder list.
- result = QualityManager.getInstance().findAllUsableQualitiesForMachineAndExtruders(global_container_stack, extruder_stacks)
-
- # The usable quality types are set
- quality_type_set = set([x.getMetaDataEntry("quality_type") for x in result])
-
- # Fetch all qualities available for this machine and the materials selected in extruders
- all_qualities = QualityManager.getInstance().findAllQualitiesForMachineAndMaterials(global_stack_definition, materials)
-
- # If in the all qualities there is some of them that are not available due to incompatibility with materials
- # we also add it so that they will appear in the slide quality bar. However in recomputeItems will be marked as
- # not available so they will be shown in gray
- for quality in all_qualities:
- if quality.getMetaDataEntry("quality_type") not in quality_type_set:
- result.append(quality)
-
- if len(result) > 1 and self._empty_quality in result:
- result.remove(self._empty_quality)
-
- return {item.getId(): item for item in result}, {} #Only return true profiles for now, no metadata. The quality manager is not able to get only metadata yet.
-
- ## Re-computes the items in this model, and adds the layer height role.
- def _recomputeItems(self):
- # Some globals that we can re-use.
- global_container_stack = Application.getInstance().getGlobalContainerStack()
- if global_container_stack is None:
- return
-
- extruder_stacks = self._getOrderedExtruderStacksList()
- container_registry = ContainerRegistry.getInstance()
-
- # Get a list of usable/available qualities for this machine and material
- qualities = QualityManager.getInstance().findAllUsableQualitiesForMachineAndExtruders(global_container_stack, extruder_stacks)
-
- unit = global_container_stack.getBottom().getProperty("layer_height", "unit")
- if not unit:
- unit = ""
-
- # group all quality items according to quality_types, so we know which profile suits the currently
- # active machine and material, and later yield the right ones.
- tmp_all_quality_items = OrderedDict()
- for item in super()._recomputeItems():
- profiles = container_registry.findContainersMetadata(id = item["id"])
- if not profiles or "quality_type" not in profiles[0]:
- quality_type = ""
- else:
- quality_type = profiles[0]["quality_type"]
-
- if quality_type not in tmp_all_quality_items:
- tmp_all_quality_items[quality_type] = {"suitable_container": None, "all_containers": []}
-
- tmp_all_quality_items[quality_type]["all_containers"].append(item)
- if tmp_all_quality_items[quality_type]["suitable_container"] is None:
- tmp_all_quality_items[quality_type]["suitable_container"] = item
-
- # reverse the ordering (finest first, coarsest last)
- all_quality_items = OrderedDict()
- for key in reversed(tmp_all_quality_items.keys()):
- all_quality_items[key] = tmp_all_quality_items[key]
-
- # First the suitable containers are set in the model
- containers = []
- for data_item in all_quality_items.values():
- suitable_item = data_item["suitable_container"]
- if suitable_item is not None:
- containers.append(suitable_item)
-
- # Once the suitable containers are collected, the rest of the containers are appended
- for data_item in all_quality_items.values():
- for item in data_item["all_containers"]:
- if item not in containers:
- containers.append(item)
-
- # Now all the containers are set
- for item in containers:
- profile = container_registry.findContainers(id = item["id"])
-
- # When for some reason there is no profile container in the registry
- if not profile:
- self._setItemLayerHeight(item, "", "")
- item["available"] = False
- yield item
- continue
-
- profile = profile[0]
-
- # When there is a profile but it's an empty quality should. It's shown in the list (they are "Not Supported" profiles)
- if profile.getId() == "empty_quality":
- self._setItemLayerHeight(item, "", "")
- item["available"] = True
- yield item
- continue
-
- item["available"] = profile in qualities
-
- # Easy case: This profile defines its own layer height.
- if profile.hasProperty("layer_height", "value"):
- self._setItemLayerHeight(item, profile.getProperty("layer_height", "value"), unit)
- yield item
- continue
-
- machine_manager = Application.getInstance().getMachineManager()
-
- # Quality-changes profile that has no value for layer height. Get the corresponding quality profile and ask that profile.
- quality_type = profile.getMetaDataEntry("quality_type", None)
- if quality_type:
- quality_results = machine_manager.determineQualityAndQualityChangesForQualityType(quality_type)
- for quality_result in quality_results:
- if quality_result["stack"] is global_container_stack:
- quality = quality_result["quality"]
- break
- else:
- # No global container stack in the results:
- if quality_results:
- # Take any of the extruders.
- quality = quality_results[0]["quality"]
- else:
- quality = None
- if quality and quality.hasProperty("layer_height", "value"):
- self._setItemLayerHeight(item, quality.getProperty("layer_height", "value"), unit)
- yield item
- continue
-
- # Quality has no value for layer height either. Get the layer height from somewhere lower in the stack.
- skip_until_container = global_container_stack.material
- if not skip_until_container or skip_until_container == ContainerRegistry.getInstance().getEmptyInstanceContainer(): # No material in stack.
- skip_until_container = global_container_stack.variant
- if not skip_until_container or skip_until_container == ContainerRegistry.getInstance().getEmptyInstanceContainer(): # No variant in stack.
- skip_until_container = global_container_stack.getBottom()
- self._setItemLayerHeight(item, global_container_stack.getRawProperty("layer_height", "value", skip_until_container = skip_until_container.getId()), unit) # Fall through to the currently loaded material.
- yield item
-
- ## Get a list of extruder stacks with the active extruder at the front of the list.
- @staticmethod
- def _getOrderedExtruderStacksList() -> List["ExtruderStack"]:
- extruder_manager = ExtruderManager.getInstance()
- extruder_stacks = extruder_manager.getActiveExtruderStacks()
- active_extruder = extruder_manager.getActiveExtruderStack()
-
- if active_extruder in extruder_stacks:
- extruder_stacks.remove(active_extruder)
- extruder_stacks = [active_extruder] + extruder_stacks
-
- return extruder_stacks
-
- @staticmethod
- def _setItemLayerHeight(item, value, unit):
- item["layer_height"] = str(value) + unit
- item["layer_height_without_unit"] = str(value)
diff --git a/cura/Settings/QualityAndUserProfilesModel.py b/cura/Settings/QualityAndUserProfilesModel.py
deleted file mode 100644
index 645e63acdb..0000000000
--- a/cura/Settings/QualityAndUserProfilesModel.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (c) 2016 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-from UM.Application import Application
-from UM.Settings.ContainerRegistry import ContainerRegistry
-
-from cura.QualityManager import QualityManager
-from cura.Settings.ProfilesModel import ProfilesModel
-from cura.Settings.ExtruderManager import ExtruderManager
-
-
-## QML Model for listing the current list of valid quality and quality changes profiles.
-#
-class QualityAndUserProfilesModel(ProfilesModel):
- def __init__(self, parent = None):
- super().__init__(parent)
-
- self._empty_quality = ContainerRegistry.getInstance().findInstanceContainers(id = "empty_quality")[0]
-
- ## Fetch the list of containers to display.
- #
- # See UM.Settings.Models.InstanceContainersModel._fetchInstanceContainers().
- def _fetchInstanceContainers(self):
- global_container_stack = Application.getInstance().getGlobalContainerStack()
- if not global_container_stack:
- return {}, {}
-
- # Fetch the list of quality changes.
- quality_manager = QualityManager.getInstance()
- machine_definition = quality_manager.getParentMachineDefinition(global_container_stack.definition)
- quality_changes_list = quality_manager.findAllQualityChangesForMachine(machine_definition)
-
- extruder_manager = ExtruderManager.getInstance()
- active_extruder = extruder_manager.getActiveExtruderStack()
- extruder_stacks = self._getOrderedExtruderStacksList()
-
- # Fetch the list of usable qualities across all extruders.
- # The actual list of quality profiles come from the first extruder in the extruder list.
- quality_list = quality_manager.findAllUsableQualitiesForMachineAndExtruders(global_container_stack, extruder_stacks)
-
- # Filter the quality_change by the list of available quality_types
- quality_type_set = set([x.getMetaDataEntry("quality_type") for x in quality_list])
- # Also show custom profiles based on "Not Supported" quality profile
- quality_type_set.add(self._empty_quality.getMetaDataEntry("quality_type"))
- filtered_quality_changes = {qc.getId(): qc for qc in quality_changes_list if
- qc.getMetaDataEntry("quality_type") in quality_type_set and
- qc.getMetaDataEntry("extruder") is not None and
- (qc.getMetaDataEntry("extruder") == active_extruder.definition.getMetaDataEntry("quality_definition") or
- qc.getMetaDataEntry("extruder") == active_extruder.definition.getId())}
-
- result = filtered_quality_changes
- for q in quality_list:
- if q.getId() != "empty_quality":
- result[q.getId()] = q
- return result, {} #Only return true profiles for now, no metadata. The quality manager is not able to get only metadata yet.
diff --git a/cura/Settings/QualitySettingsModel.py b/cura/Settings/QualitySettingsModel.py
deleted file mode 100644
index fb1aa9a6b2..0000000000
--- a/cura/Settings/QualitySettingsModel.py
+++ /dev/null
@@ -1,249 +0,0 @@
-# Copyright (c) 2017 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-from PyQt5.QtCore import pyqtProperty, pyqtSignal, Qt
-
-from UM.Logger import Logger
-import UM.Qt
-from UM.Application import Application
-from UM.Settings.ContainerRegistry import ContainerRegistry
-import os
-
-from UM.i18n import i18nCatalog
-
-
-class QualitySettingsModel(UM.Qt.ListModel.ListModel):
- KeyRole = Qt.UserRole + 1
- LabelRole = Qt.UserRole + 2
- UnitRole = Qt.UserRole + 3
- ProfileValueRole = Qt.UserRole + 4
- ProfileValueSourceRole = Qt.UserRole + 5
- UserValueRole = Qt.UserRole + 6
- CategoryRole = Qt.UserRole + 7
-
- def __init__(self, parent = None):
- super().__init__(parent = parent)
-
- self._container_registry = ContainerRegistry.getInstance()
-
- self._extruder_id = None
- self._extruder_definition_id = None
- self._quality_id = None
- self._material_id = None
- self._i18n_catalog = None
-
- self.addRoleName(self.KeyRole, "key")
- self.addRoleName(self.LabelRole, "label")
- self.addRoleName(self.UnitRole, "unit")
- self.addRoleName(self.ProfileValueRole, "profile_value")
- self.addRoleName(self.ProfileValueSourceRole, "profile_value_source")
- self.addRoleName(self.UserValueRole, "user_value")
- self.addRoleName(self.CategoryRole, "category")
-
- self._empty_quality = self._container_registry.findInstanceContainers(id = "empty_quality")[0]
-
- def setExtruderId(self, extruder_id):
- if extruder_id != self._extruder_id:
- self._extruder_id = extruder_id
- self._update()
- self.extruderIdChanged.emit()
-
- extruderIdChanged = pyqtSignal()
- @pyqtProperty(str, fset = setExtruderId, notify = extruderIdChanged)
- def extruderId(self):
- return self._extruder_id
-
- def setExtruderDefinition(self, extruder_definition):
- if extruder_definition != self._extruder_definition_id:
- self._extruder_definition_id = extruder_definition
- self._update()
- self.extruderDefinitionChanged.emit()
-
- extruderDefinitionChanged = pyqtSignal()
- @pyqtProperty(str, fset = setExtruderDefinition, notify = extruderDefinitionChanged)
- def extruderDefinition(self):
- return self._extruder_definition_id
-
- def setQuality(self, quality):
- if quality != self._quality_id:
- self._quality_id = quality
- self._update()
- self.qualityChanged.emit()
-
- qualityChanged = pyqtSignal()
- @pyqtProperty(str, fset = setQuality, notify = qualityChanged)
- def quality(self):
- return self._quality_id
-
- def setMaterial(self, material):
- if material != self._material_id:
- self._material_id = material
- self._update()
- self.materialChanged.emit()
-
- materialChanged = pyqtSignal()
- @pyqtProperty(str, fset = setMaterial, notify = materialChanged)
- def material(self):
- return self._material_id
-
- def _update(self):
- if not self._quality_id:
- return
-
- items = []
-
- definition_container = Application.getInstance().getGlobalContainerStack().getBottom()
-
- containers = self._container_registry.findInstanceContainers(id = self._quality_id)
- if not containers:
- Logger.log("w", "Could not find a quality container with id %s", self._quality_id)
- return
-
- quality_container = None
- quality_changes_container = None
-
- if containers[0].getMetaDataEntry("type") == "quality":
- quality_container = containers[0]
- else:
- quality_changes_container = containers[0]
-
- if quality_changes_container.getMetaDataEntry("quality_type") == self._empty_quality.getMetaDataEntry("quality_type"):
- quality_container = self._empty_quality
- else:
- criteria = {
- "type": "quality",
- "quality_type": quality_changes_container.getMetaDataEntry("quality_type"),
- "definition": quality_changes_container.getDefinition().getId()
- }
-
- quality_container = self._container_registry.findInstanceContainers(**criteria)
- if not quality_container:
- Logger.log("w", "Could not find a quality container matching quality changes %s", quality_changes_container.getId())
- return
-
- quality_container = quality_container[0]
-
- quality_type = quality_container.getMetaDataEntry("quality_type")
-
- if quality_type == "not_supported":
- containers = []
- else:
- definition_id = Application.getInstance().getMachineManager().getQualityDefinitionId(quality_container.getDefinition())
- definition = quality_container.getDefinition()
-
- # Check if the definition container has a translation file.
- definition_suffix = ContainerRegistry.getMimeTypeForContainer(type(definition)).preferredSuffix
- catalog = i18nCatalog(os.path.basename(definition_id + "." + definition_suffix))
- if catalog.hasTranslationLoaded():
- self._i18n_catalog = catalog
-
- for file_name in quality_container.getDefinition().getInheritedFiles():
- catalog = i18nCatalog(os.path.basename(file_name))
- if catalog.hasTranslationLoaded():
- self._i18n_catalog = catalog
-
- criteria = {"type": "quality", "quality_type": quality_type, "definition": definition_id}
-
- if self._material_id and self._material_id != "empty_material":
- criteria["material"] = self._material_id
-
- criteria["extruder"] = self._extruder_id
-
- containers = self._container_registry.findInstanceContainers(**criteria)
- if not containers:
- # Try again, this time without extruder
- new_criteria = criteria.copy()
- new_criteria.pop("extruder")
- containers = self._container_registry.findInstanceContainers(**new_criteria)
-
- if not containers and "material" in criteria:
- # Try again, this time without material
- criteria.pop("material", None)
- containers = self._container_registry.findInstanceContainers(**criteria)
-
- if not containers:
- # Try again, this time without material or extruder
- criteria.pop("extruder") # "material" has already been popped
- containers = self._container_registry.findInstanceContainers(**criteria)
-
- if not containers:
- Logger.log("w", "Could not find any quality containers matching the search criteria %s" % str(criteria))
- return
-
- if quality_changes_container:
- if quality_type == "not_supported":
- criteria = {"type": "quality_changes", "quality_type": quality_type, "name": quality_changes_container.getName()}
- else:
- criteria = {"type": "quality_changes", "quality_type": quality_type, "definition": definition_id, "name": quality_changes_container.getName()}
- if self._extruder_definition_id != "":
- extruder_definitions = self._container_registry.findDefinitionContainers(id = self._extruder_definition_id)
- if extruder_definitions:
- criteria["extruder"] = Application.getInstance().getMachineManager().getQualityDefinitionId(extruder_definitions[0])
- criteria["name"] = quality_changes_container.getName()
- else:
- criteria["extruder"] = None
-
- changes = self._container_registry.findInstanceContainers(**criteria)
- if changes:
- containers.extend(changes)
-
- global_container_stack = Application.getInstance().getGlobalContainerStack()
-
- current_category = ""
- for definition in definition_container.findDefinitions():
- if definition.type == "category":
- current_category = definition.label
- if self._i18n_catalog:
- current_category = self._i18n_catalog.i18nc(definition.key + " label", definition.label)
- continue
-
- profile_value = None
- profile_value_source = ""
- for container in containers:
- new_value = container.getProperty(definition.key, "value")
-
- if new_value is not None:
- profile_value_source = container.getMetaDataEntry("type")
- profile_value = new_value
-
- # Global tab should use resolve (if there is one)
- if not self._extruder_id:
- resolve_value = global_container_stack.getProperty(definition.key, "resolve")
- if resolve_value is not None and profile_value is not None and profile_value_source != "quality_changes":
- profile_value = resolve_value
-
- user_value = None
- if not self._extruder_id:
- user_value = global_container_stack.getTop().getProperty(definition.key, "value")
- else:
- extruder_stack = self._container_registry.findContainerStacks(id = self._extruder_id)
- if extruder_stack:
- user_value = extruder_stack[0].getTop().getProperty(definition.key, "value")
-
- if profile_value is None and user_value is None:
- continue
-
- settable_per_extruder = global_container_stack.getProperty(definition.key, "settable_per_extruder")
- # If a setting is not settable per extruder (global) and we're looking at an extruder tab, don't show this value.
- if self._extruder_id != "" and not settable_per_extruder:
- continue
-
- # If a setting is settable per extruder (not global) and we're looking at global tab, don't show this value.
- if self._extruder_id == "" and settable_per_extruder:
- continue
-
- label = definition.label
- if self._i18n_catalog:
- label = self._i18n_catalog.i18nc(definition.key + " label", label)
-
- items.append({
- "key": definition.key,
- "label": label,
- "unit": definition.unit,
- "profile_value": "" if profile_value is None else str(profile_value), # it is for display only
- "profile_value_source": profile_value_source,
- "user_value": "" if user_value is None else str(user_value),
- "category": current_category
- })
-
- self.setItems(items)
diff --git a/cura/Settings/SettingInheritanceManager.py b/cura/Settings/SettingInheritanceManager.py
index 0d4cd02cdb..e317b20f68 100644
--- a/cura/Settings/SettingInheritanceManager.py
+++ b/cura/Settings/SettingInheritanceManager.py
@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
-from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal
+from PyQt5.QtCore import QObject, QTimer, pyqtProperty, pyqtSignal
from UM.FlameProfiler import pyqtSlot
from UM.Application import Application
from UM.Logger import Logger
@@ -30,6 +30,11 @@ class SettingInheritanceManager(QObject):
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged)
self._onActiveExtruderChanged()
+ self._update_timer = QTimer()
+ self._update_timer.setInterval(500)
+ self._update_timer.setSingleShot(True)
+ self._update_timer.timeout.connect(self._update)
+
settingsWithIntheritanceChanged = pyqtSignal()
## Get the keys of all children settings with an override.
@@ -226,9 +231,7 @@ class SettingInheritanceManager(QObject):
self._onActiveExtruderChanged()
def _onContainersChanged(self, container):
- # TODO: Multiple container changes in sequence now cause quite a few recalculations.
- # This isn't that big of an issue, but it could be in the future.
- self._update()
+ self._update_timer.start()
@staticmethod
def createSettingInheritanceManager(engine=None, script_engine=None):
diff --git a/cura/Settings/SettingOverrideDecorator.py b/cura/Settings/SettingOverrideDecorator.py
index b853c06c8e..e853a3a979 100644
--- a/cura/Settings/SettingOverrideDecorator.py
+++ b/cura/Settings/SettingOverrideDecorator.py
@@ -32,9 +32,11 @@ class SettingOverrideDecorator(SceneNodeDecorator):
def __init__(self):
super().__init__()
- self._stack = PerObjectContainerStack(stack_id = "per_object_stack_" + str(id(self)))
+ self._stack = PerObjectContainerStack(container_id = "per_object_stack_" + str(id(self)))
self._stack.setDirty(False) # This stack does not need to be saved.
- self._stack.addContainer(InstanceContainer(container_id = "SettingOverrideInstanceContainer"))
+ user_container = InstanceContainer(container_id = "SettingOverrideInstanceContainer")
+ user_container.addMetaDataEntry("type", "user")
+ self._stack.userChanges = user_container
self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(0).getId()
self._is_non_printing_mesh = False
@@ -61,7 +63,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
# use value from the stack because there can be a delay in signal triggering and "_is_non_printing_mesh"
# has not been updated yet.
- deep_copy._is_non_printing_mesh = any(bool(self._stack.getProperty(setting, "value")) for setting in self._non_printing_mesh_settings)
+ deep_copy._is_non_printing_mesh = self.evaluateIsNonPrintingMesh()
return deep_copy
@@ -89,10 +91,13 @@ class SettingOverrideDecorator(SceneNodeDecorator):
def isNonPrintingMesh(self):
return self._is_non_printing_mesh
+ def evaluateIsNonPrintingMesh(self):
+ return any(bool(self._stack.getProperty(setting, "value")) for setting in self._non_printing_mesh_settings)
+
def _onSettingChanged(self, instance, property_name): # Reminder: 'property' is a built-in function
- # Trigger slice/need slicing if the value has changed.
if property_name == "value":
- self._is_non_printing_mesh = any(bool(self._stack.getProperty(setting, "value")) for setting in self._non_printing_mesh_settings)
+ # Trigger slice/need slicing if the value has changed.
+ self._is_non_printing_mesh = self.evaluateIsNonPrintingMesh()
Application.getInstance().getBackend().needsSlicing()
Application.getInstance().getBackend().tickle()
diff --git a/cura/Settings/SimpleModeSettingsManager.py b/cura/Settings/SimpleModeSettingsManager.py
index 867a21702c..a337d8b04e 100644
--- a/cura/Settings/SimpleModeSettingsManager.py
+++ b/cura/Settings/SimpleModeSettingsManager.py
@@ -16,7 +16,8 @@ class SimpleModeSettingsManager(QObject):
self._is_profile_user_created = False # True when profile was custom created by user
self._machine_manager.activeStackValueChanged.connect(self._updateIsProfileCustomized)
- self._machine_manager.activeQualityChanged.connect(self._updateIsProfileUserCreated)
+ self._machine_manager.activeQualityGroupChanged.connect(self._updateIsProfileUserCreated)
+ self._machine_manager.activeQualityChangesGroupChanged.connect(self._updateIsProfileUserCreated)
# update on create as the activeQualityChanged signal is emitted before this manager is created when Cura starts
self._updateIsProfileCustomized()
diff --git a/cura/Settings/UserProfilesModel.py b/cura/Settings/UserProfilesModel.py
deleted file mode 100644
index 6605f52f8a..0000000000
--- a/cura/Settings/UserProfilesModel.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# Copyright (c) 2017 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-from UM.Application import Application
-from UM.Settings.ContainerRegistry import ContainerRegistry
-
-from cura.QualityManager import QualityManager
-from cura.Settings.ProfilesModel import ProfilesModel
-from cura.Settings.ExtruderManager import ExtruderManager
-
-## QML Model for listing the current list of valid quality changes profiles.
-#
-class UserProfilesModel(ProfilesModel):
- def __init__(self, parent = None):
- super().__init__(parent)
-
- #Need to connect to the metaDataChanged signal of the active materials.
- self.__current_extruders = []
- self.__current_materials = []
-
- Application.getInstance().getExtruderManager().extrudersChanged.connect(self.__onExtrudersChanged)
- self.__onExtrudersChanged()
- self.__current_materials = [extruder.material for extruder in self.__current_extruders]
- for material in self.__current_materials:
- material.metaDataChanged.connect(self._onContainerChanged)
-
- self._empty_quality = ContainerRegistry.getInstance().findContainers(id = "empty_quality")[0]
-
- ## Fetch the list of containers to display.
- #
- # See UM.Settings.Models.InstanceContainersModel._fetchInstanceContainers().
- def _fetchInstanceContainers(self):
- global_container_stack = Application.getInstance().getGlobalContainerStack()
- if not global_container_stack:
- return {}, {}
-
- # Fetch the list of quality changes.
- quality_manager = QualityManager.getInstance()
- machine_definition = quality_manager.getParentMachineDefinition(global_container_stack.definition)
- quality_changes_list = quality_manager.findAllQualityChangesForMachine(machine_definition)
-
- extruder_manager = ExtruderManager.getInstance()
- active_extruder = extruder_manager.getActiveExtruderStack()
- extruder_stacks = self._getOrderedExtruderStacksList()
-
- # Fetch the list of usable qualities across all extruders.
- # The actual list of quality profiles come from the first extruder in the extruder list.
- quality_list = quality_manager.findAllUsableQualitiesForMachineAndExtruders(global_container_stack, extruder_stacks)
-
- # Filter the quality_change by the list of available quality_types
- quality_type_set = set([x.getMetaDataEntry("quality_type") for x in quality_list])
- quality_type_set.add(self._empty_quality.getMetaDataEntry("quality_type"))
-
- filtered_quality_changes = {qc.getId():qc for qc in quality_changes_list if
- qc.getMetaDataEntry("quality_type") in quality_type_set and
- qc.getMetaDataEntry("extruder") is not None and
- (qc.getMetaDataEntry("extruder") == active_extruder.definition.getMetaDataEntry("quality_definition") or
- qc.getMetaDataEntry("extruder") == active_extruder.definition.getId())}
-
- return filtered_quality_changes, {} #Only return true profiles for now, no metadata. The quality manager is not able to get only metadata yet.
-
- ## Called when a container changed on an extruder stack.
- #
- # If it's the material we need to connect to the metaDataChanged signal of
- # that.
- def __onContainerChanged(self, new_container):
- #Careful not to update when a quality or quality changes profile changed!
- #If you then update you're going to have an infinite recursion because the update may change the container.
- if new_container.getMetaDataEntry("type") == "material":
- for material in self.__current_materials:
- material.metaDataChanged.disconnect(self._onContainerChanged)
- self.__current_materials = [extruder.material for extruder in self.__current_extruders]
- for material in self.__current_materials:
- material.metaDataChanged.connect(self._onContainerChanged)
-
- ## Called when the current set of extruders change.
- #
- # This makes sure that we are listening to the signal for when the
- # materials change.
- def __onExtrudersChanged(self):
- for extruder in self.__current_extruders:
- extruder.containersChanged.disconnect(self.__onContainerChanged)
- self.__current_extruders = Application.getInstance().getExtruderManager().getExtruderStacks()
- for extruder in self.__current_extruders:
- extruder.containersChanged.connect(self.__onContainerChanged)
\ No newline at end of file
diff --git a/cura/Snapshot.py b/cura/Snapshot.py
index f12ff3e0e1..1f2a24aecd 100644
--- a/cura/Snapshot.py
+++ b/cura/Snapshot.py
@@ -3,6 +3,7 @@
import numpy
from PyQt5 import QtCore
+from PyQt5.QtGui import QImage
from cura.PreviewPass import PreviewPass
from cura.Scene import ConvexHullNode
@@ -17,11 +18,31 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
class Snapshot:
+ @staticmethod
+ def getImageBoundaries(image: QImage):
+ # Look at the resulting image to get a good crop.
+ # Get the pixels as byte array
+ pixel_array = image.bits().asarray(image.byteCount())
+ width, height = image.width(), image.height()
+ # Convert to numpy array, assume it's 32 bit (it should always be)
+ pixels = numpy.frombuffer(pixel_array, dtype=numpy.uint8).reshape([height, width, 4])
+ # Find indices of non zero pixels
+ nonzero_pixels = numpy.nonzero(pixels)
+ min_y, min_x, min_a_ = numpy.amin(nonzero_pixels, axis=1)
+ max_y, max_x, max_a_ = numpy.amax(nonzero_pixels, axis=1)
+
+ return min_x, max_x, min_y, max_y
+
+ ## Return a QImage of the scene
+ # Uses PreviewPass that leaves out some elements
+ # Aspect ratio assumes a square
@staticmethod
def snapshot(width = 300, height = 300):
scene = Application.getInstance().getController().getScene()
active_camera = scene.getActiveCamera()
render_width, render_height = active_camera.getWindowSize()
+ render_width = int(render_width)
+ render_height = int(render_height)
preview_pass = PreviewPass(render_width, render_height)
root = scene.getRoot()
@@ -29,83 +50,53 @@ class Snapshot:
# determine zoom and look at
bbox = None
- hulls = None
for node in DepthFirstIterator(root):
- if type(node) == ConvexHullNode:
- print(node)
if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible():
if bbox is None:
bbox = node.getBoundingBox()
else:
bbox = bbox + node.getBoundingBox()
- convex_hull = node.getMeshData().getConvexHullTransformedVertices(node.getWorldTransformation())
- if hulls is None:
- hulls = convex_hull
- else:
- hulls = numpy.concatenate((hulls, convex_hull), axis = 0)
+ # If there is no bounding box, it means that there is no model in the buildplate
if bbox is None:
- bbox = AxisAlignedBox()
+ return None
look_at = bbox.center
+ # guessed size so the objects are hopefully big
size = max(bbox.width, bbox.height, bbox.depth * 0.5)
- # Somehow the aspect ratio is also influenced in reverse by the screen width/height
- # So you have to set it to render_width/render_height to get 1
- projection_matrix = Matrix()
- projection_matrix.setPerspective(30, render_width / render_height, 1, 500)
- camera.setProjectionMatrix(projection_matrix)
-
- looking_from_offset = Vector(1, 1, 2)
+ # Looking from this direction (x, y, z) in OGL coordinates
+ looking_from_offset = Vector(-1, 1, 2)
if size > 0:
# determine the watch distance depending on the size
looking_from_offset = looking_from_offset * size * 1.3
- camera.setViewportSize(render_width, render_height)
- camera.setWindowSize(render_width, render_height)
camera.setPosition(look_at + looking_from_offset)
camera.lookAt(look_at)
- # we need this for the projection calculation
- hulls4 = numpy.ones((hulls.shape[0], 4))
- hulls4[:, :-1] = hulls
- #position = Vector(10, 10, 10)
- # projected_position = camera.project(position)
+ satisfied = False
+ size = None
+ fovy = 30
- preview_pass.setCamera(camera)
- preview_pass.setSize(render_width, render_height) # texture size
- preview_pass.render()
- pixel_output = preview_pass.getOutput()
+ while not satisfied:
+ if size is not None:
+ satisfied = True # always be satisfied after second try
+ projection_matrix = Matrix()
+ # Somehow the aspect ratio is also influenced in reverse by the screen width/height
+ # So you have to set it to render_width/render_height to get 1
+ projection_matrix.setPerspective(fovy, render_width / render_height, 1, 500)
+ camera.setProjectionMatrix(projection_matrix)
+ preview_pass.setCamera(camera)
+ preview_pass.render()
+ pixel_output = preview_pass.getOutput()
- print("Calculating image coordinates...")
- view = camera.getWorldTransformation().getInverse()
- min_x, max_x, min_y, max_y = render_width, 0, render_height, 0
- for hull_coords in hulls4:
- projected_position = view.getData().dot(hull_coords)
- projected_position2 = projection_matrix.getData().dot(projected_position)
- #xx, yy = camera.project(Vector(data = hull_coords))
- # xx, yy range from -1 to 1
- xx = projected_position2[0] / projected_position2[2] / 2.0
- yy = projected_position2[1] / projected_position2[2] / 2.0
- # x, y 0..render_width/height
- x = int(render_width / 2 + xx * render_width / 2)
- y = int(render_height / 2 + yy * render_height / 2)
- min_x = min(x, min_x)
- max_x = max(x, max_x)
- min_y = min(y, min_y)
- max_y = max(y, max_y)
- print(min_x, max_x, min_y, max_y)
+ min_x, max_x, min_y, max_y = Snapshot.getImageBoundaries(pixel_output)
- # print("looping all pixels in python...")
- # min_x_, max_x_, min_y_, max_y_ = render_width, 0, render_height, 0
- # for y in range(int(render_height)):
- # for x in range(int(render_width)):
- # color = pixel_output.pixelColor(x, y)
- # if color.alpha() > 0:
- # min_x_ = min(x, min_x_)
- # max_x_ = max(x, max_x_)
- # min_y_ = min(y, min_y_)
- # max_y_ = max(y, max_y_)
- # print(min_x_, max_x_, min_y_, max_y_)
+ size = max((max_x - min_x) / render_width, (max_y - min_y) / render_height)
+ if size > 0.5 or satisfied:
+ satisfied = True
+ else:
+ # make it big and allow for some empty space around
+ fovy *= 0.5 # strangely enough this messes up the aspect ratio: fovy *= size * 1.1
# make it a square
if max_x - min_x >= max_y - min_y:
@@ -114,11 +105,12 @@ class Snapshot:
else:
# make x bigger
min_x, max_x = int((max_x + min_x) / 2 - (max_y - min_y) / 2), int((max_x + min_x) / 2 + (max_y - min_y) / 2)
- copy_pixel_output = pixel_output.copy(min_x, min_y, max_x - min_x, max_y - min_y)
+ cropped_image = pixel_output.copy(min_x, min_y, max_x - min_x, max_y - min_y)
- # Scale it to the correct height
- image = copy_pixel_output.scaledToHeight(height, QtCore.Qt.SmoothTransformation)
- # Then chop of the width
- cropped_image = image.copy(image.width() // 2 - width // 2, 0, width, height)
+ # Scale it to the correct size
+ scaled_image = cropped_image.scaled(
+ width, height,
+ aspectRatioMode = QtCore.Qt.IgnoreAspectRatio,
+ transformMode = QtCore.Qt.SmoothTransformation)
- return cropped_image
+ return scaled_image
diff --git a/cura_app.py b/cura_app.py
index b9afb9bbcc..6c2d1c2937 100755
--- a/cura_app.py
+++ b/cura_app.py
@@ -16,6 +16,12 @@ parser.add_argument('--debug',
default = False,
help = "Turn on the debug mode by setting this option."
)
+parser.add_argument('--trigger-early-crash',
+ dest = 'trigger_early_crash',
+ action = 'store_true',
+ default = False,
+ help = "FOR TESTING ONLY. Trigger an early crash to show the crash dialog."
+ )
known_args = vars(parser.parse_known_args()[0])
if not known_args["debug"]:
@@ -26,7 +32,7 @@ if not known_args["debug"]:
return os.path.expanduser("~/.local/share/cura")
elif Platform.isOSX():
return os.path.expanduser("~/Library/Logs/cura")
-
+
if hasattr(sys, "frozen"):
dirpath = get_cura_dir_path()
os.makedirs(dirpath, exist_ok = True)
@@ -71,8 +77,45 @@ if "PYTHONPATH" in os.environ.keys(): # If PYTHONPATH is u
def exceptHook(hook_type, value, traceback):
from cura.CrashHandler import CrashHandler
- _crash_handler = CrashHandler(hook_type, value, traceback)
- _crash_handler.show()
+ from cura.CuraApplication import CuraApplication
+ has_started = False
+ if CuraApplication.Created:
+ has_started = CuraApplication.getInstance().started
+
+ #
+ # When the exception hook is triggered, the QApplication may not have been initialized yet. In this case, we don't
+ # have an QApplication to handle the event loop, which is required by the Crash Dialog.
+ # The flag "CuraApplication.Created" is set to True when CuraApplication finishes its constructor call.
+ #
+ # Before the "started" flag is set to True, the Qt event loop has not started yet. The event loop is a blocking
+ # call to the QApplication.exec_(). In this case, we need to:
+ # 1. Remove all scheduled events so no more unnecessary events will be processed, such as loading the main dialog,
+ # loading the machine, etc.
+ # 2. Start the Qt event loop with exec_() and show the Crash Dialog.
+ #
+ # If the application has finished its initialization and was running fine, and then something causes a crash,
+ # we run the old routine to show the Crash Dialog.
+ #
+ from PyQt5.Qt import QApplication
+ if CuraApplication.Created:
+ _crash_handler = CrashHandler(hook_type, value, traceback, has_started)
+ if CuraApplication.splash is not None:
+ CuraApplication.splash.close()
+ if not has_started:
+ CuraApplication.getInstance().removePostedEvents(None)
+ _crash_handler.early_crash_dialog.show()
+ sys.exit(CuraApplication.getInstance().exec_())
+ else:
+ _crash_handler.show()
+ else:
+ application = QApplication(sys.argv)
+ application.removePostedEvents(None)
+ _crash_handler = CrashHandler(hook_type, value, traceback, has_started)
+ # This means the QtApplication could be created and so the splash screen. Then Cura closes it
+ if CuraApplication.splash is not None:
+ CuraApplication.splash.close()
+ _crash_handler.early_crash_dialog.show()
+ sys.exit(application.exec_())
if not known_args["debug"]:
sys.excepthook = exceptHook
diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py
index 1726818100..6c2fb9a59d 100755
--- a/plugins/3MFReader/ThreeMFReader.py
+++ b/plugins/3MFReader/ThreeMFReader.py
@@ -4,26 +4,27 @@
import os.path
import zipfile
+import numpy
+
+import Savitar
+
+from UM.Application import Application
from UM.Logger import Logger
from UM.Math.Matrix import Matrix
from UM.Math.Vector import Vector
from UM.Mesh.MeshBuilder import MeshBuilder
from UM.Mesh.MeshReader import MeshReader
from UM.Scene.GroupDecorator import GroupDecorator
-from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
-from UM.Application import Application
+
from cura.Settings.ExtruderManager import ExtruderManager
-from cura.QualityManager import QualityManager
from cura.Scene.CuraSceneNode import CuraSceneNode
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
from cura.Scene.ZOffsetDecorator import ZOffsetDecorator
+from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
MYPY = False
-import Savitar
-import numpy
-
try:
if not MYPY:
import xml.etree.cElementTree as ET
@@ -77,9 +78,9 @@ class ThreeMFReader(MeshReader):
self._object_count += 1
node_name = "Object %s" % self._object_count
- active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
+ active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
- um_node = CuraSceneNode()
+ um_node = CuraSceneNode() # This adds a SettingOverrideDecorator
um_node.addDecorator(BuildPlateDecorator(active_build_plate))
um_node.setName(node_name)
transformation = self._createMatrixFromTransformationString(savitar_node.getTransformation())
@@ -108,8 +109,6 @@ class ThreeMFReader(MeshReader):
# Add the setting override decorator, so we can add settings to this node.
if settings:
- um_node.addDecorator(SettingOverrideDecorator())
-
global_container_stack = Application.getInstance().getGlobalContainerStack()
# Ensure the correct next container for the SettingOverride decorator is set.
@@ -120,8 +119,8 @@ class ThreeMFReader(MeshReader):
um_node.callDecoration("setActiveExtruder", default_stack.getId())
# Get the definition & set it
- definition = QualityManager.getInstance().getParentMachineDefinition(global_container_stack.getBottom())
- um_node.callDecoration("getStack").getTop().setDefinition(definition.getId())
+ definition_id = getMachineDefinitionIDForQualitySearch(global_container_stack.definition)
+ um_node.callDecoration("getStack").getTop().setDefinition(definition_id)
setting_container = um_node.callDecoration("getStack").getTop()
@@ -138,7 +137,7 @@ class ThreeMFReader(MeshReader):
continue
setting_container.setProperty(key, "value", setting_value)
- if len(um_node.getChildren()) > 0:
+ if len(um_node.getChildren()) > 0 and um_node.getMeshData() is None:
group_decorator = GroupDecorator()
um_node.addDecorator(group_decorator)
um_node.setSelectable(True)
diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py
index 01de4cc2c5..633142187c 100755
--- a/plugins/3MFReader/ThreeMFWorkspaceReader.py
+++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py
@@ -1,11 +1,19 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
+from configparser import ConfigParser
+import zipfile
+import os
+import threading
+
+import xml.etree.ElementTree as ET
+
from UM.Workspace.WorkspaceReader import WorkspaceReader
from UM.Application import Application
from UM.Logger import Logger
from UM.i18n import i18nCatalog
+from UM.Signal import postponeSignals, CompressTechnique
from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.DefinitionContainer import DefinitionContainer
from UM.Settings.InstanceContainer import InstanceContainer
@@ -13,25 +21,14 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.MimeTypeDatabase import MimeTypeDatabase
from UM.Job import Job
from UM.Preferences import Preferences
-from UM.Util import parseBool
-from .WorkspaceDialog import WorkspaceDialog
-
-import xml.etree.ElementTree as ET
from cura.Settings.CuraStackBuilder import CuraStackBuilder
-from cura.Settings.ExtruderManager import ExtruderManager
from cura.Settings.ExtruderStack import ExtruderStack
from cura.Settings.GlobalStack import GlobalStack
from cura.Settings.CuraContainerStack import _ContainerIndexes
-from cura.QualityManager import QualityManager
from cura.CuraApplication import CuraApplication
-from configparser import ConfigParser
-import zipfile
-import io
-import configparser
-import os
-import threading
+from .WorkspaceDialog import WorkspaceDialog
i18n_catalog = i18nCatalog("cura")
@@ -67,6 +64,49 @@ def call_on_qt_thread(func):
return _call_on_qt_thread_wrapper
+class ContainerInfo:
+ def __init__(self, file_name: str, serialized: str, parser: ConfigParser):
+ self.file_name = file_name
+ self.serialized = serialized
+ self.parser = parser
+ self.container = None
+ self.definition_id = None
+
+
+class QualityChangesInfo:
+ def __init__(self):
+ self.name = None
+ self.global_info = None
+ self.extruder_info_dict = {}
+
+
+class MachineInfo:
+ def __init__(self):
+ self.container_id = None
+ self.name = None
+ self.definition_id = None
+ self.quality_type = None
+ self.custom_quality_name = None
+ self.quality_changes_info = None
+ self.variant_info = None
+
+ self.definition_changes_info = None
+ self.user_changes_info = None
+
+ self.extruder_info_dict = {}
+
+
+class ExtruderInfo:
+ def __init__(self):
+ self.position = None
+ self.enabled = True
+ self.variant_info = None
+ self.root_material_id = None
+
+ self.definition_changes_info = None
+ self.user_changes_info = None
+
+
## Base implementation for reading 3MF workspace files.
class ThreeMFWorkspaceReader(WorkspaceReader):
def __init__(self):
@@ -97,6 +137,18 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# 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._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):
@@ -122,7 +174,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# The default ContainerStack.deserialize() will connect signals, which is not desired in this case.
# Since we know that the stack files are INI files, so we directly use the ConfigParser to parse them.
serialized = archive.open(file_name).read().decode("utf-8")
- stack_config = ConfigParser()
+ stack_config = ConfigParser(interpolation = None)
stack_config.read_string(serialized)
# sanity check
@@ -148,6 +200,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# \param file_name
# \param show_dialog In case we use preRead() to check if a file is a valid project file, we don't want to show a dialog.
def preRead(self, file_name, show_dialog=True, *args, **kwargs):
+ self._clearState()
+
self._3mf_mesh_reader = Application.getInstance().getMeshFileHandler().getReaderForFile(file_name)
if self._3mf_mesh_reader and self._3mf_mesh_reader.preRead(file_name) == WorkspaceReader.PreReadResult.accepted:
pass
@@ -155,6 +209,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
Logger.log("w", "Could not find reader that was able to read the scene data for 3MF workspace")
return WorkspaceReader.PreReadResult.failed
+ self._machine_info = MachineInfo()
machine_type = ""
variant_type_name = i18n_catalog.i18nc("@label", "Nozzle")
@@ -162,11 +217,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
archive = zipfile.ZipFile(file_name, "r")
cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")]
- # A few lists of containers in this project files.
- # When loading the global stack file, it may be associated with those containers, which may or may not be
- # in Cura already, so we need to provide them as alternative search lists.
- instance_container_list = []
-
resolve_strategy_keys = ["machine", "material", "quality_changes"]
self._resolve_strategies = {k: None for k in resolve_strategy_keys}
containers_found_dict = {k: False for k in resolve_strategy_keys}
@@ -174,23 +224,23 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
#
# Read definition containers
#
+ machine_definition_id = None
machine_definition_container_count = 0
extruder_definition_container_count = 0
definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)]
- for each_definition_container_file in definition_container_files:
- container_id = self._stripFileToId(each_definition_container_file)
+ for definition_container_file in definition_container_files:
+ container_id = self._stripFileToId(definition_container_file)
definitions = self._container_registry.findDefinitionContainersMetadata(id = container_id)
+ serialized = archive.open(definition_container_file).read().decode("utf-8")
if not definitions:
- definition_container = DefinitionContainer(container_id)
- definition_container.deserialize(archive.open(each_definition_container_file).read().decode("utf-8"), file_name = each_definition_container_file)
- definition_container = definition_container.getMetaData()
-
+ definition_container = DefinitionContainer.deserializeMetadata(serialized, container_id)[0]
else:
definition_container = definitions[0]
definition_container_type = definition_container.get("type")
if definition_container_type == "machine":
+ machine_definition_id = container_id
machine_type = definition_container["name"]
variant_type_name = definition_container.get("variants_name", variant_type_name)
@@ -199,22 +249,29 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
extruder_definition_container_count += 1
else:
Logger.log("w", "Unknown definition container type %s for %s",
- definition_container_type, each_definition_container_file)
+ definition_container_type, definition_container_file)
Job.yieldThread()
if machine_definition_container_count != 1:
- return WorkspaceReader.PreReadResult.failed #Not a workspace file but ordinary 3MF.
+ return WorkspaceReader.PreReadResult.failed # Not a workspace file but ordinary 3MF.
material_labels = []
material_conflict = False
xml_material_profile = self._getXmlProfileClass()
+ reverse_material_id_dict = {}
if self._material_container_suffix is None:
self._material_container_suffix = ContainerRegistry.getMimeTypeForContainer(xml_material_profile).preferredSuffix
if xml_material_profile:
material_container_files = [name for name in cura_file_names if name.endswith(self._material_container_suffix)]
for material_container_file in material_container_files:
container_id = self._stripFileToId(material_container_file)
- material_labels.append(self._getMaterialLabelFromSerialized(archive.open(material_container_file).read().decode("utf-8")))
+
+ serialized = archive.open(material_container_file).read().decode("utf-8")
+ metadata_list = xml_material_profile.deserializeMetadata(serialized, container_id)
+ reverse_map = {metadata["id"]: container_id for metadata in metadata_list}
+ reverse_material_id_dict.update(reverse_map)
+
+ material_labels.append(self._getMaterialLabelFromSerialized(serialized))
if self._container_registry.findContainersMetadata(id = container_id): #This material already exists.
containers_found_dict["material"] = True
if not self._container_registry.isReadOnly(container_id): # Only non readonly materials can be in conflict
@@ -224,49 +281,60 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# Check if any quality_changes instance container is in conflict.
instance_container_files = [name for name in cura_file_names if name.endswith(self._instance_container_suffix)]
quality_name = ""
- quality_type = ""
+ custom_quality_name = ""
num_settings_overriden_by_quality_changes = 0 # How many settings are changed by the quality changes
- num_settings_overriden_by_definition_changes = 0 # How many settings are changed by the definition changes
num_user_settings = 0
quality_changes_conflict = False
- definition_changes_conflict = False
- for each_instance_container_file in instance_container_files:
- container_id = self._stripFileToId(each_instance_container_file)
- instance_container = InstanceContainer(container_id)
+ self._machine_info.quality_changes_info = QualityChangesInfo()
- # Deserialize InstanceContainer by converting read data from bytes to string
- instance_container.deserialize(archive.open(each_instance_container_file).read().decode("utf-8"),
- file_name = each_instance_container_file)
- instance_container_list.append(instance_container)
+ quality_changes_info_list = []
+ instance_container_info_dict = {} # id -> parser
+ for instance_container_file_name in instance_container_files:
+ container_id = self._stripFileToId(instance_container_file_name)
- container_type = instance_container.getMetaDataEntry("type")
+ serialized = archive.open(instance_container_file_name).read().decode("utf-8")
+
+ # Qualities and variants don't have upgrades, so don't upgrade them
+ parser = ConfigParser(interpolation = None)
+ parser.read_string(serialized)
+ container_type = parser["metadata"]["type"]
+ if container_type not in ("quality", "variant"):
+ serialized = InstanceContainer._updateSerialized(serialized, instance_container_file_name)
+
+ parser = ConfigParser(interpolation = None)
+ parser.read_string(serialized)
+ container_info = ContainerInfo(instance_container_file_name, serialized, parser)
+ instance_container_info_dict[container_id] = container_info
+
+ container_type = parser["metadata"]["type"]
if container_type == "quality_changes":
- quality_name = instance_container.getName()
- num_settings_overriden_by_quality_changes += len(instance_container._instances)
+ quality_changes_info_list.append(container_info)
+
+ if not parser.has_option("metadata", "position"):
+ self._machine_info.quality_changes_info.name = parser["general"]["name"]
+ self._machine_info.quality_changes_info.global_info = container_info
+ else:
+ position = parser["metadata"]["position"]
+ self._machine_info.quality_changes_info.extruder_info_dict[position] = container_info
+
+ custom_quality_name = parser["general"]["name"]
+ values = parser["values"] if parser.has_section("values") else dict()
+ num_settings_overriden_by_quality_changes += len(values)
# Check if quality changes already exists.
quality_changes = self._container_registry.findInstanceContainers(id = container_id)
if quality_changes:
containers_found_dict["quality_changes"] = True
# Check if there really is a conflict by comparing the values
+ instance_container = InstanceContainer(container_id)
+ instance_container.deserialize(serialized, file_name = instance_container_file_name)
if quality_changes[0] != instance_container:
quality_changes_conflict = True
- elif container_type == "definition_changes":
- definition_name = instance_container.getName()
- num_settings_overriden_by_definition_changes += len(instance_container._instances)
- # Check if definition changes already exists.
- definition_changes = self._container_registry.findInstanceContainers(id = container_id)
- # Check if there is any difference the loaded settings from the project file and the settings in Cura.
- if definition_changes:
- containers_found_dict["definition_changes"] = True
- # Check if there really is a conflict by comparing the values
- if definition_changes[0] != instance_container:
- definition_changes_conflict = True
elif container_type == "quality":
if not quality_name:
- quality_name = instance_container.getName()
+ quality_name = parser["general"]["name"]
elif container_type == "user":
- num_user_settings += len(instance_container._instances)
+ num_user_settings += len(parser["values"])
elif container_type in self._ignored_instance_container_types:
# Ignore certain instance container types
Logger.log("w", "Ignoring instance container [%s] with type [%s]", container_id, container_type)
@@ -274,6 +342,9 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
Job.yieldThread()
+ if self._machine_info.quality_changes_info.global_info is None:
+ self._machine_info.quality_changes_info = None
+
# Load ContainerStack files and ExtruderStack files
global_stack_file, extruder_stack_files = self._determineGlobalAndExtruderStackFiles(
file_name, cura_file_names)
@@ -282,12 +353,15 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# - the global stack exists but some/all of the extruder stacks DON'T exist
# - the global stack DOESN'T exist but some/all of the extruder stacks exist
# To simplify this, only check if the global stack exists or not
- container_id = self._stripFileToId(global_stack_file)
+ global_stack_id = self._stripFileToId(global_stack_file)
serialized = archive.open(global_stack_file).read().decode("utf-8")
machine_name = self._getMachineNameFromSerializedStack(serialized)
- stacks = self._container_registry.findContainerStacks(id = container_id)
+ stacks = self._container_registry.findContainerStacks(name = machine_name, type = "machine")
+ self._is_same_machine_type = True
+ existing_global_stack = None
if stacks:
global_stack = stacks[0]
+ existing_global_stack = global_stack
containers_found_dict["machine"] = True
# Check if there are any changes at all in any of the container stacks.
id_list = self._getContainerIdListFromSerialized(serialized)
@@ -297,30 +371,81 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
if global_stack.getContainer(index).getId() != container_id:
machine_conflict = True
break
+ self._is_same_machine_type = global_stack.definition.getId() == machine_definition_id
+
+ # Get quality type
+ parser = ConfigParser(interpolation = None)
+ parser.read_string(serialized)
+ quality_container_id = parser["containers"][str(_ContainerIndexes.Quality)]
+ quality_type = instance_container_info_dict[quality_container_id].parser["metadata"]["quality_type"]
+
+ # Get machine info
+ serialized = archive.open(global_stack_file).read().decode("utf-8")
+ serialized = GlobalStack._updateSerialized(serialized, global_stack_file)
+ parser = ConfigParser(interpolation = None)
+ parser.read_string(serialized)
+ definition_changes_id = parser["containers"][str(_ContainerIndexes.DefinitionChanges)]
+ if definition_changes_id not in ("empty", "empty_definition_changes"):
+ self._machine_info.definition_changes_info = instance_container_info_dict[definition_changes_id]
+ user_changes_id = parser["containers"][str(_ContainerIndexes.UserChanges)]
+ if user_changes_id not in ("empty", "empty_user_changes"):
+ self._machine_info.user_changes_info = instance_container_info_dict[user_changes_id]
+
+ # Also check variant and material in case it doesn't have extruder stacks
+ if not extruder_stack_files:
+ position = "0"
+
+ extruder_info = ExtruderInfo()
+ extruder_info.position = position
+ variant_id = parser["containers"][str(_ContainerIndexes.Variant)]
+ material_id = parser["containers"][str(_ContainerIndexes.Material)]
+ if variant_id not in ("empty", "empty_variant"):
+ extruder_info.variant_info = instance_container_info_dict[variant_id]
+ if material_id not in ("empty", "empty_material"):
+ root_material_id = reverse_material_id_dict[material_id]
+ extruder_info.root_material_id = root_material_id
+ self._machine_info.extruder_info_dict[position] = extruder_info
+ else:
+ variant_id = parser["containers"][str(_ContainerIndexes.Variant)]
+ if variant_id not in ("empty", "empty_variant"):
+ self._machine_info.variant_info = instance_container_info_dict[variant_id]
+
Job.yieldThread()
# if the global stack is found, we check if there are conflicts in the extruder stacks
- if containers_found_dict["machine"] and not machine_conflict:
- for extruder_stack_file in extruder_stack_files:
- serialized = archive.open(extruder_stack_file).read().decode("utf-8")
- parser = configparser.ConfigParser()
- parser.read_string(serialized)
+ for extruder_stack_file in extruder_stack_files:
+ serialized = archive.open(extruder_stack_file).read().decode("utf-8")
+ serialized = ExtruderStack._updateSerialized(serialized, extruder_stack_file)
+ parser = ConfigParser(interpolation = None)
+ parser.read_string(serialized)
- # The check should be done for the extruder stack that's associated with the existing global stack,
- # and those extruder stacks may have different IDs.
- # So we check according to the positions
+ # The check should be done for the extruder stack that's associated with the existing global stack,
+ # and those extruder stacks may have different IDs.
+ # So we check according to the positions
+ position = parser["metadata"]["position"]
+ variant_id = parser["containers"][str(_ContainerIndexes.Variant)]
+ material_id = parser["containers"][str(_ContainerIndexes.Material)]
- position = str(parser["metadata"]["position"])
+ extruder_info = ExtruderInfo()
+ extruder_info.position = position
+ if parser.has_option("metadata", "enabled"):
+ extruder_info.enabled = parser["metadata"]["enabled"]
+ if variant_id not in ("empty", "empty_variant"):
+ extruder_info.variant_info = instance_container_info_dict[variant_id]
+ if material_id not in ("empty", "empty_material"):
+ root_material_id = reverse_material_id_dict[material_id]
+ extruder_info.root_material_id = root_material_id
+ definition_changes_id = parser["containers"][str(_ContainerIndexes.DefinitionChanges)]
+ if definition_changes_id not in ("empty", "empty_definition_changes"):
+ extruder_info.definition_changes_info = instance_container_info_dict[definition_changes_id]
+ user_changes_id = parser["containers"][str(_ContainerIndexes.UserChanges)]
+ if user_changes_id not in ("empty", "empty_user_changes"):
+ extruder_info.user_changes_info = instance_container_info_dict[user_changes_id]
+ self._machine_info.extruder_info_dict[position] = extruder_info
+
+ if not machine_conflict and containers_found_dict["machine"]:
if position not in global_stack.extruders:
- # The extruder position defined in the project doesn't exist in this global stack.
- # We can say that it is a machine conflict, but it is very hard to override the machine in this
- # case because we need to override the existing extruders and add the non-existing extruders.
- #
- # HACK:
- # To make this simple, we simply say that there is no machine conflict and create a new machine
- # by default.
- machine_conflict = False
- break
+ continue
existing_extruder_stack = global_stack.extruders[position]
# check if there are any changes at all in any of the container stacks.
@@ -361,10 +486,28 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
extruders = num_extruders * [""]
+ quality_name = custom_quality_name if custom_quality_name else quality_name
+
+ self._machine_info.container_id = global_stack_id
+ self._machine_info.name = machine_name
+ self._machine_info.definition_id = machine_definition_id
+ self._machine_info.quality_type = quality_type
+ self._machine_info.custom_quality_name = quality_name
+
+ if machine_conflict and not self._is_same_machine_type:
+ machine_conflict = False
+
+ is_printer_group = False
+ if machine_conflict:
+ group_name = existing_global_stack.getMetaDataEntry("connect_group_name")
+ if group_name is not None:
+ is_printer_group = True
+ machine_name = group_name
+
# Show the dialog, informing the user what is about to happen.
self._dialog.setMachineConflict(machine_conflict)
+ self._dialog.setIsPrinterGroup(is_printer_group)
self._dialog.setQualityChangesConflict(quality_changes_conflict)
- self._dialog.setDefinitionChangesConflict(definition_changes_conflict)
self._dialog.setMaterialConflict(material_conflict)
self._dialog.setHasVisibleSettingsField(has_visible_settings_string)
self._dialog.setNumVisibleSettings(num_visible_settings)
@@ -407,7 +550,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
## Overrides an ExtruderStack in the given GlobalStack and returns the new ExtruderStack.
def _overrideExtruderStack(self, global_stack, extruder_file_content, extruder_stack_file):
# Get extruder position first
- extruder_config = configparser.ConfigParser()
+ extruder_config = ConfigParser(interpolation = None)
extruder_config.read_string(extruder_file_content)
if not extruder_config.has_option("metadata", "position"):
msg = "Could not find 'metadata/position' in extruder stack file"
@@ -435,6 +578,27 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# \param file_name
@call_on_qt_thread
def read(self, file_name):
+ container_registry = ContainerRegistry.getInstance()
+ signals = [container_registry.containerAdded,
+ container_registry.containerRemoved,
+ container_registry.containerMetaDataChanged]
+ #
+ # We now have different managers updating their lookup tables upon container changes. It is critical to make
+ # sure that the managers have a complete set of data when they update.
+ #
+ # In project loading, lots of the container-related signals are loosely emitted, which can create timing gaps
+ # for incomplete data update or other kinds of issues to happen.
+ #
+ # To avoid this, we postpone all signals so they don't get emitted immediately. But, please also be aware that,
+ # because of this, do not expect to have the latest data in the lookup tables in project loading.
+ #
+ with postponeSignals(*signals, compress = CompressTechnique.NoCompression):
+ return self._read(file_name)
+
+ def _read(self, file_name):
+ application = CuraApplication.getInstance()
+ material_manager = application.getMaterialManager()
+
archive = zipfile.ZipFile(file_name, "r")
cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")]
@@ -453,6 +617,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
Logger.log("w", "Workspace did not contain visible settings. Leaving visibility unchanged")
else:
global_preferences.setValue("general/visible_settings", visible_settings)
+ global_preferences.setValue("cura/active_setting_visibility_preset", "custom")
categories_expanded = temp_preferences.getValue("cura/categories_expanded")
if categories_expanded is None:
@@ -460,52 +625,24 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
else:
global_preferences.setValue("cura/categories_expanded", categories_expanded)
- Application.getInstance().expandedCategoriesChanged.emit() # Notify the GUI of the change
+ application.expandedCategoriesChanged.emit() # Notify the GUI of the change
- self._id_mapping = {}
+ # If a machine with the same name is of a different type, always create a new one.
+ if not self._is_same_machine_type or self._resolve_strategies["machine"] != "override":
+ # We need to create a new machine
+ machine_name = self._container_registry.uniqueName(self._machine_info.name)
- # We don't add containers right away, but wait right until right before the stack serialization.
- # We do this so that if something goes wrong, it's easier to clean up.
- containers_to_add = []
+ global_stack = CuraStackBuilder.createMachine(machine_name, self._machine_info.definition_id)
+ extruder_stack_dict = global_stack.extruders
- global_stack_file, extruder_stack_files = self._determineGlobalAndExtruderStackFiles(file_name, cura_file_names)
+ self._container_registry.addContainer(global_stack)
+ else:
+ # Find the machine
+ global_stack = self._container_registry.findContainerStacks(name = self._machine_info.name, type = "machine")[0]
+ extruder_stacks = self._container_registry.findContainerStacks(machine = global_stack.getId(),
+ type = "extruder_train")
+ extruder_stack_dict = {stack.getMetaDataEntry("position"): stack for stack in extruder_stacks}
- global_stack = None
- extruder_stacks = []
- extruder_stacks_added = []
- container_stacks_added = []
- machine_extruder_count = None
-
- containers_added = []
-
- global_stack_id_original = self._stripFileToId(global_stack_file)
- global_stack_id_new = global_stack_id_original
- global_stack_name_original = self._getMachineNameFromSerializedStack(archive.open(global_stack_file).read().decode("utf-8"))
- global_stack_name_new = global_stack_name_original
- global_stack_need_rename = False
-
- extruder_stack_id_map = {} # new and old ExtruderStack IDs map
- if self._resolve_strategies["machine"] == "new":
- # We need a new id if the id already exists
- if self._container_registry.findContainerStacksMetadata(id = global_stack_id_original):
- global_stack_id_new = self.getNewId(global_stack_id_original)
- global_stack_need_rename = True
-
- if self._container_registry.findContainerStacksMetadata(name = global_stack_id_original):
- global_stack_name_new = self._container_registry.uniqueName(global_stack_name_original)
-
- for each_extruder_stack_file in extruder_stack_files:
- old_container_id = self._stripFileToId(each_extruder_stack_file)
- new_container_id = old_container_id
- if self._container_registry.findContainerStacksMetadata(id = old_container_id):
- # get a new name for this extruder
- new_container_id = self.getNewId(old_container_id)
-
- extruder_stack_id_map[old_container_id] = new_container_id
-
- # TODO: For the moment we use pretty naive existence checking. If the ID is the same, we assume in quite a few
- # TODO: cases that the container loaded is the same (most notable in materials & definitions).
- # TODO: It might be possible that we need to add smarter checking in the future.
Logger.log("d", "Workspace loading is checking definitions...")
# Get all the definition files & check if they exist. If not, add them.
definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)]
@@ -520,7 +657,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
Job.yieldThread()
Logger.log("d", "Workspace loading is checking materials...")
- material_containers = []
# Get all the material files and check if they exist. If not, add them.
xml_material_profile = self._getXmlProfileClass()
if self._material_container_suffix is None:
@@ -528,537 +664,57 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
if xml_material_profile:
material_container_files = [name for name in cura_file_names if name.endswith(self._material_container_suffix)]
for material_container_file in material_container_files:
+ to_deserialize_material = False
container_id = self._stripFileToId(material_container_file)
+ need_new_name = False
materials = self._container_registry.findInstanceContainers(id = container_id)
if not materials:
- material_container = xml_material_profile(container_id)
- material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"),
- file_name = material_container_file)
- containers_to_add.append(material_container)
+ # No material found, deserialize this material later and add it
+ to_deserialize_material = True
else:
material_container = materials[0]
- if not self._container_registry.isReadOnly(container_id): # Only create new materials if they are not read only.
+ 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.
+ to_deserialize_material = True
+
if self._resolve_strategies["material"] == "override":
- material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"),
- file_name = material_container_file)
+ # Remove the old materials and then deserialize the one from the project
+ root_material_id = material_container.getMetaDataEntry("base_file")
+ material_manager.removeMaterialByRootId(root_material_id)
elif self._resolve_strategies["material"] == "new":
# Note that we *must* deserialize it with a new ID, as multiple containers will be
# auto created & added.
- material_container = xml_material_profile(self.getNewId(container_id))
- material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"),
- file_name = material_container_file)
- containers_to_add.append(material_container)
+ container_id = self.getNewId(container_id)
+ self._old_new_materials[old_material_root_id] = container_id
+ need_new_name = True
- material_containers.append(material_container)
+ if to_deserialize_material:
+ material_container = xml_material_profile(container_id)
+ material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"),
+ file_name = container_id + "." + self._material_container_suffix)
+ if need_new_name:
+ new_name = ContainerRegistry.getInstance().uniqueName(material_container.getName())
+ material_container.setName(new_name)
+ material_container.setDirty(True)
+ self._container_registry.addContainer(material_container)
Job.yieldThread()
- Logger.log("d", "Workspace loading is checking instance containers...")
- # Get quality_changes and user profiles saved in the workspace
- instance_container_files = [name for name in cura_file_names if name.endswith(self._instance_container_suffix)]
- user_instance_containers = []
- quality_and_definition_changes_instance_containers = []
- quality_changes_instance_containers = []
- for instance_container_file in instance_container_files:
- container_id = self._stripFileToId(instance_container_file)
- serialized = archive.open(instance_container_file).read().decode("utf-8")
+ # Handle quality changes if any
+ self._processQualityChanges(global_stack)
- # HACK! we ignore "quality" and "variant" instance containers!
- parser = configparser.ConfigParser()
- parser.read_string(serialized)
- if not parser.has_option("metadata", "type"):
- Logger.log("w", "Cannot find metadata/type in %s, ignoring it", instance_container_file)
- continue
- if parser.get("metadata", "type") in self._ignored_instance_container_types:
- continue
-
- instance_container = InstanceContainer(container_id)
-
- # Deserialize InstanceContainer by converting read data from bytes to string
- instance_container.deserialize(serialized, file_name = instance_container_file)
- container_type = instance_container.getMetaDataEntry("type")
- Job.yieldThread()
-
- #
- # IMPORTANT:
- # If an instance container (or maybe other type of container) exists, and user chooses "Create New",
- # we need to rename this container and all references to it, and changing those references are VERY
- # HARD.
- #
- if container_type in self._ignored_instance_container_types:
- # Ignore certain instance container types
- Logger.log("w", "Ignoring instance container [%s] with type [%s]", container_id, container_type)
- continue
- elif container_type == "user":
- # Check if quality changes already exists.
- user_containers = self._container_registry.findInstanceContainers(id = container_id)
- if not user_containers:
- containers_to_add.append(instance_container)
- else:
- if self._resolve_strategies["machine"] == "override" or self._resolve_strategies["machine"] is None:
- instance_container = user_containers[0]
- instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8"),
- file_name = instance_container_file)
- instance_container.setDirty(True)
- elif self._resolve_strategies["machine"] == "new":
- # The machine is going to get a spiffy new name, so ensure that the id's of user settings match.
- old_extruder_id = instance_container.getMetaDataEntry("extruder", None)
- if old_extruder_id:
- new_extruder_id = extruder_stack_id_map[old_extruder_id]
- new_id = new_extruder_id + "_current_settings"
- instance_container.setMetaDataEntry("id", new_id)
- instance_container.setName(new_id)
- instance_container.setMetaDataEntry("extruder", new_extruder_id)
- containers_to_add.append(instance_container)
-
- machine_id = instance_container.getMetaDataEntry("machine", None)
- if machine_id:
- new_machine_id = self.getNewId(machine_id)
- new_id = new_machine_id + "_current_settings"
- instance_container.setMetaDataEntry("id", new_id)
- instance_container.setName(new_id)
- instance_container.setMetaDataEntry("machine", new_machine_id)
- containers_to_add.append(instance_container)
- user_instance_containers.append(instance_container)
- elif container_type in ("quality_changes", "definition_changes"):
- # Check if quality changes already exists.
- changes_containers = self._container_registry.findInstanceContainers(id = container_id)
- if not changes_containers:
- # no existing containers with the same ID, so we can safely add the new one
- containers_to_add.append(instance_container)
- else:
- # we have found existing container with the same ID, so we need to resolve according to the
- # selected strategy.
- if self._resolve_strategies[container_type] == "override":
- instance_container = changes_containers[0]
- instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8"),
- file_name = instance_container_file)
- instance_container.setDirty(True)
-
- elif self._resolve_strategies[container_type] == "new":
- # TODO: how should we handle the case "new" for quality_changes and definition_changes?
-
- instance_container.setName(self._container_registry.uniqueName(instance_container.getName()))
- new_changes_container_id = self.getNewId(instance_container.getId())
- instance_container.setMetaDataEntry("id", new_changes_container_id)
-
- # TODO: we don't know the following is correct or not, need to verify
- # AND REFACTOR!!!
- if self._resolve_strategies["machine"] == "new":
- # The machine is going to get a spiffy new name, so ensure that the id's of user settings match.
- old_extruder_id = instance_container.getMetaDataEntry("extruder", None)
- # Note that in case of a quality_changes extruder means the definition id of the extruder stack
- # For the user settings, it means the actual extruder stack id it's assigned to.
- if old_extruder_id and old_extruder_id in extruder_stack_id_map:
- new_extruder_id = extruder_stack_id_map[old_extruder_id]
- instance_container.setMetaDataEntry("extruder", new_extruder_id)
-
- machine_id = instance_container.getMetaDataEntry("machine", None)
- if machine_id:
- new_machine_id = self.getNewId(machine_id)
- instance_container.setMetaDataEntry("machine", new_machine_id)
-
- containers_to_add.append(instance_container)
-
- elif self._resolve_strategies[container_type] is None:
- # The ID already exists, but nothing in the values changed, so do nothing.
- pass
- quality_and_definition_changes_instance_containers.append(instance_container)
- if container_type == "quality_changes":
- quality_changes_instance_containers.append(instance_container)
-
- if container_type == "definition_changes":
- definition_changes_extruder_count = instance_container.getProperty("machine_extruder_count", "value")
- if definition_changes_extruder_count is not None:
- machine_extruder_count = definition_changes_extruder_count
-
- else:
- existing_container = self._container_registry.findInstanceContainersMetadata(id = container_id)
- if not existing_container:
- containers_to_add.append(instance_container)
- if global_stack_need_rename:
- if instance_container.getMetaDataEntry("machine"):
- instance_container.setMetaDataEntry("machine", global_stack_id_new)
-
- # Add all the containers right before we try to add / serialize the stack
- for container in containers_to_add:
- self._container_registry.addContainer(container)
- container.setDirty(True)
- containers_added.append(container)
-
- # Get the stack(s) saved in the workspace.
- Logger.log("d", "Workspace loading is checking stacks containers...")
-
- # load global stack file
- try:
- stack = None
-
- if self._resolve_strategies["machine"] == "override":
- container_stacks = self._container_registry.findContainerStacks(id = global_stack_id_original)
- stack = container_stacks[0]
-
- # HACK
- # There is a machine, check if it has authentication data. If so, keep that data.
- network_authentication_id = stack.getMetaDataEntry("network_authentication_id")
- network_authentication_key = stack.getMetaDataEntry("network_authentication_key")
- stack.deserialize(archive.open(global_stack_file).read().decode("utf-8"), file_name = global_stack_file)
- if network_authentication_id:
- stack.addMetaDataEntry("network_authentication_id", network_authentication_id)
- if network_authentication_key:
- stack.addMetaDataEntry("network_authentication_key", network_authentication_key)
-
- elif self._resolve_strategies["machine"] == "new":
- # create a new global stack
- stack = GlobalStack(global_stack_id_new)
- # Deserialize stack by converting read data from bytes to string
- stack.deserialize(archive.open(global_stack_file).read().decode("utf-8"),
- file_name = global_stack_file)
-
- # Ensure a unique ID and name
- stack.setMetaDataEntry("id", global_stack_id_new)
-
- # Only machines need a new name, stacks may be non-unique
- stack.setName(global_stack_name_new)
-
- container_stacks_added.append(stack)
- # self._container_registry.addContainer(stack)
- containers_added.append(stack)
- else:
- Logger.log("e", "Resolve strategy of %s for machine is not supported", self._resolve_strategies["machine"])
-
- # Create a new definition_changes container if it was empty
- if stack.definitionChanges == self._container_registry.getEmptyInstanceContainer():
- stack.setDefinitionChanges(CuraStackBuilder.createDefinitionChangesContainer(stack, stack.getId() + "_settings"))
- global_stack = stack
- Job.yieldThread()
- except:
- Logger.logException("w", "We failed to serialize the stack. Trying to clean up.")
- # Something went really wrong. Try to remove any data that we added.
- for container in containers_added:
- self._container_registry.removeContainer(container.getId())
- return
-
- # load extruder stack files
- has_extruder_stack_files = len(extruder_stack_files) > 0
- empty_quality_container = self._container_registry.findInstanceContainers(id = "empty_quality")[0]
- empty_quality_changes_container = self._container_registry.findInstanceContainers(id = "empty_quality_changes")[0]
- try:
- for extruder_stack_file in extruder_stack_files:
- container_id = self._stripFileToId(extruder_stack_file)
- extruder_file_content = archive.open(extruder_stack_file, "r").read().decode("utf-8")
-
- if self._resolve_strategies["machine"] == "override":
- # deserialize new extruder stack over the current ones (if any)
- stack = self._overrideExtruderStack(global_stack, extruder_file_content, extruder_stack_file)
- if stack is None:
- continue
-
- elif self._resolve_strategies["machine"] == "new":
- new_id = extruder_stack_id_map[container_id]
- stack = ExtruderStack(new_id)
-
- # HACK: the global stack can have a new name, so we need to make sure that this extruder stack
- # references to the new name instead of the old one. Normally, this can be done after
- # deserialize() by setting the metadata, but in the case of ExtruderStack, deserialize()
- # also does addExtruder() to its machine stack, so we have to make sure that it's pointing
- # to the right machine BEFORE deserialization.
- extruder_config = configparser.ConfigParser()
- extruder_config.read_string(extruder_file_content)
- extruder_config.set("metadata", "machine", global_stack_id_new)
- tmp_string_io = io.StringIO()
- extruder_config.write(tmp_string_io)
- extruder_file_content = tmp_string_io.getvalue()
-
- stack.deserialize(extruder_file_content, file_name = extruder_stack_file)
-
- # Ensure a unique ID and name
- stack.setMetaDataEntry("id", new_id)
-
- self._container_registry.addContainer(stack)
- extruder_stacks_added.append(stack)
- containers_added.append(stack)
- else:
- Logger.log("w", "Unknown resolve strategy: %s", self._resolve_strategies["machine"])
-
- # Create a new definition_changes container if it was empty
- if stack.definitionChanges == self._container_registry.getEmptyInstanceContainer():
- stack.setDefinitionChanges(CuraStackBuilder.createDefinitionChangesContainer(stack, stack.getId() + "_settings"))
-
- if stack.getMetaDataEntry("type") == "extruder_train":
- extruder_stacks.append(stack)
-
- # If not extruder stacks were saved in the project file (pre 3.1) create one manually
- # We re-use the container registry's addExtruderStackForSingleExtrusionMachine method for this
- if not extruder_stacks:
- # If we choose to override a machine but to create a new custom quality profile, the custom quality
- # profile is not immediately applied to the global_stack, so this fix for single extrusion machines
- # will use the current custom quality profile on the existing machine. The extra optional argument
- # in that function is used in this case to specify a new global stack quality_changes container so
- # the fix can correctly create and copy over the custom quality settings to the newly created extruder.
- new_global_quality_changes = None
- if self._resolve_strategies["quality_changes"] == "new" and len(quality_changes_instance_containers) > 0:
- new_global_quality_changes = quality_changes_instance_containers[0]
-
- # Depending if the strategy is to create a new or override, the ids must be or not be unique
- stack = self._container_registry.addExtruderStackForSingleExtrusionMachine(global_stack, "fdmextruder",
- new_global_quality_changes,
- create_new_ids = self._resolve_strategies["machine"] == "new")
- if new_global_quality_changes is not None:
- quality_changes_instance_containers.append(stack.qualityChanges)
- quality_and_definition_changes_instance_containers.append(stack.qualityChanges)
- if global_stack.quality.getId() in ("empty", "empty_quality"):
- stack.quality = empty_quality_container
- if self._resolve_strategies["machine"] == "override":
- # in case the extruder is newly created (for a single-extrusion machine), we need to override
- # the existing extruder stack.
- existing_extruder_stack = global_stack.extruders[stack.getMetaDataEntry("position")]
- for idx in range(len(_ContainerIndexes.IndexTypeMap)):
- existing_extruder_stack.replaceContainer(idx, stack._containers[idx], postpone_emit = True)
- extruder_stacks.append(existing_extruder_stack)
- else:
- extruder_stacks.append(stack)
-
- except:
- Logger.logException("w", "We failed to serialize the stack. Trying to clean up.")
- # Something went really wrong. Try to remove any data that we added.
- for container in containers_added:
- self._container_registry.removeContainer(container.getId())
- return
-
- ## In case there is a new machine and once the extruders are created, the global stack is added to the registry,
- # otherwise the accContainers function in CuraContainerRegistry will create an extruder stack and then creating
- # useless files
- if self._resolve_strategies["machine"] == "new":
- self._container_registry.addContainer(global_stack)
-
- # Check quality profiles to make sure that if one stack has the "not supported" quality profile,
- # all others should have the same.
- #
- # This block code tries to fix the following problems in Cura 3.0 and earlier:
- # 1. The upgrade script can rename all "Not Supported" quality profiles to "empty_quality", but it cannot fix
- # the problem that the global stack the extruder stacks may have different quality profiles. The code
- # below loops over all stacks and make sure that if there is one stack with "Not Supported" profile, the
- # rest should also use the "Not Supported" profile.
- # 2. In earlier versions (at least 2.7 and 3.0), a wrong quality profile could be assigned to a stack. For
- # example, a UM3 can have a BB 0.8 variant with "aa04_pla_fast" quality profile enabled. To fix this,
- # in the code below we also check the actual available quality profiles for the machine.
- #
- has_not_supported = False
- for stack in [global_stack] + extruder_stacks:
- if stack.quality.getId() in ("empty", "empty_quality"):
- has_not_supported = True
- break
-
- # We filter out extruder stacks that are not actually used, for example the UM3 and custom FDM printer extruder count setting.
- extruder_stacks_in_use = extruder_stacks
- if machine_extruder_count is not None:
- extruder_stacks_in_use = extruder_stacks[:machine_extruder_count]
-
- available_quality = QualityManager.getInstance().findAllUsableQualitiesForMachineAndExtruders(global_stack,
- extruder_stacks_in_use)
- if not has_not_supported:
- has_not_supported = not available_quality
-
- quality_has_been_changed = False
-
- if has_not_supported:
- for stack in [global_stack] + extruder_stacks_in_use:
- stack.replaceContainer(_ContainerIndexes.Quality, empty_quality_container)
- stack.replaceContainer(_ContainerIndexes.QualityChanges, empty_quality_changes_container)
- quality_has_been_changed = True
-
- else:
- # The machine in the project has non-empty quality and there are usable qualities for this machine.
- # We need to check if the current quality_type is still usable for this machine, if not, then the quality
- # will be reset to the "preferred quality" if present, otherwise "normal".
- available_quality_types = [q.getMetaDataEntry("quality_type") for q in available_quality]
-
- if global_stack.quality.getMetaDataEntry("quality_type") not in available_quality_types:
- # We are here because the quality_type specified in the project is not supported any more,
- # so we need to switch it to the "preferred quality" if present, otherwise "normal".
- quality_has_been_changed = True
-
- # find the preferred quality
- preferred_quality_id = global_stack.getMetaDataEntry("preferred_quality", None)
- if preferred_quality_id is not None:
- definition_id = global_stack.definition.getId()
- definition_id = global_stack.definition.getMetaDataEntry("quality_definition", definition_id)
- if not parseBool(global_stack.getMetaDataEntry("has_machine_quality", "False")):
- definition_id = "fdmprinter"
-
- containers = self._container_registry.findInstanceContainers(id = preferred_quality_id,
- type = "quality",
- definition = definition_id)
- containers = [c for c in containers if not c.getMetaDataEntry("material", "")]
- if containers:
- global_stack.quality = containers[0]
- global_stack.qualityChanges = empty_quality_changes_container
- # also find the quality containers for the extruders
- for extruder_stack in extruder_stacks_in_use:
- search_criteria = {"id": preferred_quality_id,
- "type": "quality",
- "definition": definition_id}
- if global_stack.getMetaDataEntry("has_machine_materials") and extruder_stack.material.getId() not in ("empty", "empty_material"):
- search_criteria["material"] = extruder_stack.material.getId()
- containers = self._container_registry.findInstanceContainers(**search_criteria)
- if containers:
- extruder_stack.quality = containers[0]
- extruder_stack.qualityChanges = empty_quality_changes_container
- else:
- Logger.log("e", "Cannot find preferred quality for extruder [%s].", extruder_stack.getId())
-
- else:
- # we cannot find the preferred quality. THIS SHOULD NOT HAPPEN
- Logger.log("e", "Cannot find the preferred quality for machine [%s]", global_stack.getId())
- else:
- # The quality_type specified in the project file is usable, but the quality container itself may not
- # be correct. For example, for UM2, the quality container can be "draft" while it should be "um2_draft"
- # instead.
- # In this code branch, we try to fix those incorrect quality containers.
- #
- # ***IMPORTANT***: We only do this fix for single-extrusion machines.
- # We will first find the correct quality profile for the extruder, then apply the same
- # quality profile for the global stack.
- #
- if len(extruder_stacks) == 1:
- extruder_stack = extruder_stacks[0]
-
- search_criteria = {"type": "quality", "quality_type": global_stack.quality.getMetaDataEntry("quality_type")}
- search_criteria["definition"] = global_stack.definition.getId()
- if not parseBool(global_stack.getMetaDataEntry("has_machine_quality", "False")):
- search_criteria["definition"] = "fdmprinter"
-
- if global_stack.getMetaDataEntry("has_machine_materials") and extruder_stack.material.getId() not in ("empty", "empty_material"):
- search_criteria["material"] = extruder_stack.material.getId()
- containers = self._container_registry.findInstanceContainers(**search_criteria)
- if containers:
- new_quality_container = containers[0]
- extruder_stack.quality = new_quality_container
- global_stack.quality = new_quality_container
-
- # Replacing the old containers if resolve is "new".
- # When resolve is "new", some containers will get renamed, so all the other containers that reference to those
- # MUST get updated too.
- #
- if self._resolve_strategies["machine"] == "new":
- # A new machine was made, but it was serialized with the wrong user container. Fix that now.
- for container in user_instance_containers:
- # replacing the container ID for user instance containers for the extruders
- extruder_id = container.getMetaDataEntry("extruder", None)
- if extruder_id:
- for extruder in extruder_stacks:
- if extruder.getId() == extruder_id:
- extruder.userChanges = container
- continue
-
- # replacing the container ID for user instance containers for the machine
- machine_id = container.getMetaDataEntry("machine", None)
- if machine_id:
- if global_stack.getId() == machine_id:
- global_stack.userChanges = container
- continue
-
- changes_container_types = ("quality_changes", "definition_changes")
- if quality_has_been_changed:
- # DO NOT replace quality_changes if the current quality_type is not supported
- changes_container_types = ("definition_changes",)
- for changes_container_type in changes_container_types:
- if self._resolve_strategies[changes_container_type] == "new":
- # Quality changes needs to get a new ID, added to registry and to the right stacks
- for each_changes_container in quality_and_definition_changes_instance_containers:
- # NOTE: The renaming and giving new IDs are possibly redundant because they are done in the
- # instance container loading part.
- new_id = each_changes_container.getId()
-
- # Find the old (current) changes container in the global stack
- if changes_container_type == "quality_changes":
- old_container = global_stack.qualityChanges
- elif changes_container_type == "definition_changes":
- old_container = global_stack.definitionChanges
-
- # sanity checks
- # NOTE: The following cases SHOULD NOT happen!!!!
- if old_container.getId() in ("empty_quality_changes", "empty_definition_changes", "empty"):
- Logger.log("e", "We try to get [%s] from the global stack [%s] but we got None instead!",
- changes_container_type, global_stack.getId())
- continue
-
- # Replace the quality/definition changes container if it's in the GlobalStack
- # NOTE: we can get an empty container here, but the IDs will not match,
- # so this comparison is fine.
- if self._id_mapping.get(old_container.getId()) == new_id:
- if changes_container_type == "quality_changes":
- global_stack.qualityChanges = each_changes_container
- elif changes_container_type == "definition_changes":
- global_stack.definitionChanges = each_changes_container
- continue
-
- # Replace the quality/definition changes container if it's in one of the ExtruderStacks
- # Only apply the change if we have loaded extruder stacks from the project
- if has_extruder_stack_files:
- for each_extruder_stack in extruder_stacks:
- changes_container = None
- if changes_container_type == "quality_changes":
- changes_container = each_extruder_stack.qualityChanges
- elif changes_container_type == "definition_changes":
- changes_container = each_extruder_stack.definitionChanges
-
- # sanity checks
- # NOTE: The following cases SHOULD NOT happen!!!!
- if changes_container.getId() in ("empty_quality_changes", "empty_definition_changes", "empty"):
- Logger.log("e", "We try to get [%s] from the extruder stack [%s] but we got None instead!",
- changes_container_type, each_extruder_stack.getId())
- continue
-
- # NOTE: we can get an empty container here, but the IDs will not match,
- # so this comparison is fine.
- if self._id_mapping.get(changes_container.getId()) == new_id:
- if changes_container_type == "quality_changes":
- each_extruder_stack.qualityChanges = each_changes_container
- elif changes_container_type == "definition_changes":
- each_extruder_stack.definitionChanges = each_changes_container
-
- if self._resolve_strategies["material"] == "new":
- # the actual material instance container can have an ID such as
- # __
- # which cannot be determined immediately, so here we use a HACK to find the right new material
- # instance ID:
- # - get the old material IDs for all material
- # - find the old material with the longest common prefix in ID, that's the old material
- # - update the name by replacing the old prefix with the new
- # - find the new material container and set it to the stack
- old_to_new_material_dict = {}
- for each_material in material_containers:
- # find the material's old name
- for old_id, new_id in self._id_mapping.items():
- if each_material.getId() == new_id:
- old_to_new_material_dict[old_id] = each_material
- break
-
- # replace old material in global and extruder stacks with new
- self._replaceStackMaterialWithNew(global_stack, old_to_new_material_dict)
- if extruder_stacks:
- for each_extruder_stack in extruder_stacks:
- self._replaceStackMaterialWithNew(each_extruder_stack, old_to_new_material_dict)
-
- if extruder_stacks:
- for stack in extruder_stacks:
- ExtruderManager.getInstance().registerExtruder(stack, global_stack.getId())
+ # Prepare the machine
+ self._applyChangesToMachine(global_stack, extruder_stack_dict)
Logger.log("d", "Workspace loading is notifying rest of the code of changes...")
-
- if self._resolve_strategies["machine"] == "new":
- for stack in extruder_stacks:
- stack.setNextStack(global_stack)
- stack.containersChanged.emit(stack.getTop())
- else:
- CuraApplication.getInstance().getMachineManager().activeQualityChanged.emit()
-
# Actually change the active machine.
- Application.getInstance().setGlobalContainerStack(global_stack)
-
- # Notify everything/one that is to notify about changes.
- global_stack.containersChanged.emit(global_stack.getTop())
+ #
+ # This is scheduled for later is because it depends on the Variant/Material/Qualitiy Managers to have the latest
+ # data, but those managers will only update upon a container/container metadata changed signal. Because this
+ # function is running on the main thread (Qt thread), although those "changed" signals have been emitted, but
+ # they won't take effect until this function is done.
+ # To solve this, we schedule _updateActiveMachine() for later so it will have the latest data.
+ self._updateActiveMachine(global_stack)
# Load all the nodes / meshdata of the workspace
nodes = self._3mf_mesh_reader.read(file_name)
@@ -1071,60 +727,284 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
self.setWorkspaceName(base_file_name)
return nodes
- ## HACK: Replaces the material container in the given stack with a newly created material container.
- # This function is used when the user chooses to resolve material conflicts by creating new ones.
- def _replaceStackMaterialWithNew(self, stack, old_new_material_dict):
- # The material containers in the project file are 'parent' material such as "generic_pla",
- # but a material container used in a global/extruder stack is a 'child' material,
- # such as "generic_pla_ultimaker3_AA_0.4", which can be formalised as the following:
- #
- # __
- #
- # In the project loading, when a user chooses to resolve material conflicts by creating new ones,
- # the old 'parent' material ID and the new 'parent' material ID are known, but not the child material IDs.
- # In this case, the global stack and the extruder stacks need to use the newly created material, but the
- # material containers they use are 'child' material. So, here, we need to find the right 'child' material for
- # the stacks.
- #
- # This hack approach works as follows:
- # - No matter there is a child material or not, the actual material we are looking for has the prefix
- # "", which is the old material name. For the material in a stack, we know that the new
- # material's ID will be "_blabla..", so we just need to replace the old material ID
- # with the new one to get the new 'child' material.
- # - Because the material containers have IDs such as "m #nn", if we use simple prefix matching, there can
- # be a problem in the following scenario:
- # - there are two materials in the project file, namely "m #1" and "m #11"
- # - the child materials in use are for example: "m #1_um3_aa04", "m #11_um3_aa04"
- # - if we only check for a simple prefix match, then "m #11_um3_aa04" will match with "m #1", but they
- # are not the same material
- # To avoid this, when doing the prefix matching, we use the result with the longest mactching prefix.
+ def _processQualityChanges(self, global_stack):
+ if self._machine_info.quality_changes_info is None:
+ return
- # find the old material ID
- old_material_id_in_stack = stack.material.getId()
- best_matching_old_material_id = None
- best_matching_old_material_prefix_length = -1
- for old_parent_material_id in old_new_material_dict:
- if len(old_parent_material_id) < best_matching_old_material_prefix_length:
+ application = CuraApplication.getInstance()
+ quality_manager = application.getQualityManager()
+
+ # If we have custom profiles, load them
+ quality_changes_name = self._machine_info.quality_changes_info.name
+ if self._machine_info.quality_changes_info is not None:
+ Logger.log("i", "Loading custom profile [%s] from project file",
+ self._machine_info.quality_changes_info.name)
+
+ # Get the correct extruder definition IDs for quality changes
+ from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
+ machine_definition_id_for_quality = getMachineDefinitionIDForQualitySearch(global_stack.definition)
+ machine_definition_for_quality = self._container_registry.findDefinitionContainers(id = machine_definition_id_for_quality)[0]
+
+ quality_changes_info = self._machine_info.quality_changes_info
+ quality_changes_quality_type = quality_changes_info.global_info.parser["metadata"]["quality_type"]
+
+ quality_changes_name = quality_changes_info.name
+ create_new = self._resolve_strategies.get("quality_changes") != "override"
+ if create_new:
+ container_info_dict = {None: self._machine_info.quality_changes_info.global_info}
+ container_info_dict.update(quality_changes_info.extruder_info_dict)
+
+ quality_changes_name = self._container_registry.uniqueName(quality_changes_name)
+ for position, container_info in container_info_dict.items():
+ extruder_stack = None
+ if position is not None:
+ extruder_stack = global_stack.extruders[position]
+ container = quality_manager._createQualityChanges(quality_changes_quality_type,
+ quality_changes_name,
+ global_stack, extruder_stack)
+ container_info.container = container
+ container.setDirty(True)
+ self._container_registry.addContainer(container)
+
+ Logger.log("d", "Created new quality changes container [%s]", container.getId())
+
+ else:
+ # Find the existing containers
+ quality_changes_containers = self._container_registry.findInstanceContainers(name = quality_changes_name,
+ type = "quality_changes")
+ for container in quality_changes_containers:
+ extruder_position = container.getMetaDataEntry("position")
+ if extruder_position is None:
+ quality_changes_info.global_info.container = container
+ else:
+ if extruder_position not in quality_changes_info.extruder_info_dict:
+ quality_changes_info.extruder_info_dict[extruder_position] = ContainerInfo(None, None, None)
+ container_info = quality_changes_info.extruder_info_dict[extruder_position]
+ container_info.container = container
+
+ # If there is no quality changes for any extruder, create one.
+ if not quality_changes_info.extruder_info_dict:
+ container_info = ContainerInfo(None, None, None)
+ quality_changes_info.extruder_info_dict["0"] = container_info
+ extruder_stack = global_stack.extruders["0"]
+
+ container = quality_manager._createQualityChanges(quality_changes_quality_type, quality_changes_name,
+ global_stack, extruder_stack)
+ container_info.container = container
+ container.setDirty(True)
+ self._container_registry.addContainer(container)
+
+ Logger.log("d", "Created new quality changes container [%s]", container.getId())
+
+ # Clear all existing containers
+ quality_changes_info.global_info.container.clear()
+ for container_info in quality_changes_info.extruder_info_dict.values():
+ container_info.container.clear()
+
+ # Loop over everything and override the existing containers
+ global_info = quality_changes_info.global_info
+ global_info.container.clear() # Clear all
+ for key, value in global_info.parser["values"].items():
+ if not machine_definition_for_quality.getProperty(key, "settable_per_extruder"):
+ global_info.container.setProperty(key, "value", value)
+ else:
+ quality_changes_info.extruder_info_dict["0"].container.setProperty(key, "value", value)
+
+ for position, container_info in quality_changes_info.extruder_info_dict.items():
+ if container_info.parser is None:
+ continue
+
+ if container_info.container is None:
+ extruder_stack = global_stack.extruders[position]
+ container = quality_manager._createQualityChanges(quality_changes_quality_type, quality_changes_name,
+ global_stack, extruder_stack)
+ container_info.container = container
+
+ for key, value in container_info.parser["values"].items():
+ container_info.container.setProperty(key, "value", value)
+
+ self._machine_info.quality_changes_info.name = quality_changes_name
+
+ def _clearStack(self, stack):
+ application = CuraApplication.getInstance()
+
+ stack.definitionChanges.clear()
+ stack.variant = application.empty_variant_container
+ stack.material = application.empty_material_container
+ stack.quality = application.empty_quality_container
+ stack.qualityChanges = application.empty_quality_changes_container
+ stack.userChanges.clear()
+
+ def _applyDefinitionChanges(self, global_stack, extruder_stack_dict):
+ values_to_set_for_extruders = {}
+ if self._machine_info.definition_changes_info is not None:
+ parser = self._machine_info.definition_changes_info.parser
+ for key, value in parser["values"].items():
+ if global_stack.getProperty(key, "settable_per_extruder"):
+ values_to_set_for_extruders[key] = value
+ else:
+ global_stack.definitionChanges.setProperty(key, "value", value)
+
+ for position, extruder_stack in extruder_stack_dict.items():
+ if position not in self._machine_info.extruder_info_dict:
continue
- if len(old_parent_material_id) <= len(old_material_id_in_stack):
- if old_parent_material_id == old_material_id_in_stack[0:len(old_parent_material_id)]:
- best_matching_old_material_prefix_length = len(old_parent_material_id)
- best_matching_old_material_id = old_parent_material_id
- if best_matching_old_material_id is None:
- Logger.log("w", "Cannot find any matching old material ID for stack [%s] material [%s]. Something can go wrong",
- stack.getId(), old_material_id_in_stack)
- return
+ extruder_info = self._machine_info.extruder_info_dict[position]
+ if extruder_info.definition_changes_info is None:
+ continue
+ parser = extruder_info.definition_changes_info.parser
+ for key, value in values_to_set_for_extruders.items():
+ extruder_stack.definitionChanges.setProperty(key, "value", value)
+ if parser is not None:
+ for key, value in parser["values"].items():
+ extruder_stack.definitionChanges.setProperty(key, "value", value)
- # find the new material container
- new_material_id = old_new_material_dict[best_matching_old_material_id].getId() + old_material_id_in_stack[len(best_matching_old_material_id):]
- new_material_containers = self._container_registry.findInstanceContainers(id = new_material_id, type = "material")
- if not new_material_containers:
- Logger.log("e", "Cannot find new material container [%s]", new_material_id)
- return
+ def _applyUserChanges(self, global_stack, extruder_stack_dict):
+ values_to_set_for_extruder_0 = {}
+ if self._machine_info.user_changes_info is not None:
+ parser = self._machine_info.user_changes_info.parser
+ for key, value in parser["values"].items():
+ if global_stack.getProperty(key, "settable_per_extruder"):
+ values_to_set_for_extruder_0[key] = value
+ else:
+ global_stack.userChanges.setProperty(key, "value", value)
- # replace the material in the given stack
- stack.material = new_material_containers[0]
+ for position, extruder_stack in extruder_stack_dict.items():
+ if position not in self._machine_info.extruder_info_dict:
+ continue
+
+ extruder_info = self._machine_info.extruder_info_dict[position]
+ if extruder_info.user_changes_info is not None:
+ parser = self._machine_info.extruder_info_dict[position].user_changes_info.parser
+ if position == "0":
+ for key, value in values_to_set_for_extruder_0.items():
+ extruder_stack.userChanges.setProperty(key, "value", value)
+ if parser is not None:
+ for key, value in parser["values"].items():
+ extruder_stack.userChanges.setProperty(key, "value", value)
+
+ def _applyVariants(self, global_stack, extruder_stack_dict):
+ application = CuraApplication.getInstance()
+ variant_manager = application.getVariantManager()
+
+ if self._machine_info.variant_info is not None:
+ parser = self._machine_info.variant_info.parser
+ variant_name = parser["general"]["name"]
+
+ from cura.Machines.VariantManager import VariantType
+ variant_type = VariantType.BUILD_PLATE
+
+ node = variant_manager.getVariantNode(global_stack.definition.getId(), variant_name, variant_type)
+ if node is not None:
+ global_stack.variant = node.getContainer()
+
+ for position, extruder_stack in extruder_stack_dict.items():
+ if position not in self._machine_info.extruder_info_dict:
+ continue
+ extruder_info = self._machine_info.extruder_info_dict[position]
+ if extruder_info.variant_info is None:
+ continue
+ parser = extruder_info.variant_info.parser
+
+ variant_name = parser["general"]["name"]
+ from cura.Machines.VariantManager import VariantType
+ variant_type = VariantType.NOZZLE
+
+ node = variant_manager.getVariantNode(global_stack.definition.getId(), variant_name, variant_type)
+ if node is not None:
+ extruder_stack.variant = node.getContainer()
+
+ def _applyMaterials(self, global_stack, extruder_stack_dict):
+ application = CuraApplication.getInstance()
+ material_manager = application.getMaterialManager()
+
+ # Force update lookup tables first
+ material_manager.initialize()
+
+ for position, extruder_stack in extruder_stack_dict.items():
+ if position not in self._machine_info.extruder_info_dict:
+ continue
+ extruder_info = self._machine_info.extruder_info_dict[position]
+ if extruder_info.root_material_id is None:
+ continue
+
+ root_material_id = extruder_info.root_material_id
+ root_material_id = self._old_new_materials.get(root_material_id, root_material_id)
+
+ # get material diameter of this extruder
+ machine_material_diameter = extruder_stack.materialDiameter
+ material_node = material_manager.getMaterialNode(global_stack.definition.getId(),
+ extruder_stack.variant.getName(),
+ machine_material_diameter,
+ root_material_id)
+ if material_node is not None:
+ extruder_stack.material = material_node.getContainer()
+
+ def _applyChangesToMachine(self, global_stack, extruder_stack_dict):
+ # Clear all first
+ self._clearStack(global_stack)
+ for extruder_stack in extruder_stack_dict.values():
+ self._clearStack(extruder_stack)
+
+ self._applyDefinitionChanges(global_stack, extruder_stack_dict)
+ self._applyUserChanges(global_stack, extruder_stack_dict)
+ self._applyVariants(global_stack, extruder_stack_dict)
+ self._applyMaterials(global_stack, extruder_stack_dict)
+
+ # prepare the quality to select
+ self._quality_changes_to_apply = None
+ self._quality_type_to_apply = None
+ if self._machine_info.quality_changes_info is not None:
+ self._quality_changes_to_apply = self._machine_info.quality_changes_info.name
+ else:
+ self._quality_type_to_apply = self._machine_info.quality_type
+
+ # Set enabled/disabled for extruders
+ for position, extruder_stack in extruder_stack_dict.items():
+ extruder_info = self._machine_info.extruder_info_dict.get(position)
+ if not extruder_info:
+ continue
+ if "enabled" not in extruder_stack.getMetaData():
+ extruder_stack.addMetaDataEntry("enabled", "True")
+ extruder_stack.setMetaDataEntry("enabled", str(extruder_info.enabled))
+
+ def _updateActiveMachine(self, global_stack):
+ # Actually change the active machine.
+ machine_manager = Application.getInstance().getMachineManager()
+ material_manager = Application.getInstance().getMaterialManager()
+ quality_manager = Application.getInstance().getQualityManager()
+
+ # Force update the lookup maps first
+ material_manager.initialize()
+ quality_manager.initialize()
+
+ machine_manager.setActiveMachine(global_stack.getId())
+
+ if self._quality_changes_to_apply:
+ quality_changes_group_dict = quality_manager.getQualityChangesGroups(global_stack)
+ if self._quality_changes_to_apply not in quality_changes_group_dict:
+ Logger.log("e", "Could not find quality_changes [%s]", self._quality_changes_to_apply)
+ return
+ quality_changes_group = quality_changes_group_dict[self._quality_changes_to_apply]
+ machine_manager.setQualityChangesGroup(quality_changes_group, no_dialog = True)
+ else:
+ self._quality_type_to_apply = self._quality_type_to_apply.lower()
+ quality_group_dict = quality_manager.getQualityGroups(global_stack)
+ if self._quality_type_to_apply in quality_group_dict:
+ quality_group = quality_group_dict[self._quality_type_to_apply]
+ else:
+ Logger.log("i", "Could not find quality type [%s], switch to default", self._quality_type_to_apply)
+ preferred_quality_type = global_stack.getMetaDataEntry("preferred_quality_type")
+ quality_group_dict = quality_manager.getQualityGroups(global_stack)
+ quality_group = quality_group_dict.get(preferred_quality_type)
+ if quality_group is None:
+ Logger.log("e", "Could not get preferred quality type [%s]", preferred_quality_type)
+
+ if quality_group is not None:
+ machine_manager.setQualityGroup(quality_group, no_dialog = True)
+
+ # Notify everything/one that is to notify about changes.
+ global_stack.containersChanged.emit(global_stack.getTop())
def _stripFileToId(self, file):
mime_type = MimeTypeDatabase.getMimeTypeForFile(file)
@@ -1136,7 +1016,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
## Get the list of ID's of all containers in a container stack by partially parsing it's serialized data.
def _getContainerIdListFromSerialized(self, serialized):
- parser = configparser.ConfigParser(interpolation=None, empty_lines_in_values=False)
+ parser = ConfigParser(interpolation=None, empty_lines_in_values=False)
parser.read_string(serialized)
container_ids = []
@@ -1157,7 +1037,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
return container_ids
def _getMachineNameFromSerializedStack(self, serialized):
- parser = configparser.ConfigParser(interpolation=None, empty_lines_in_values=False)
+ parser = ConfigParser(interpolation=None, empty_lines_in_values=False)
parser.read_string(serialized)
return parser["general"].get("name", "")
diff --git a/plugins/3MFReader/WorkspaceDialog.py b/plugins/3MFReader/WorkspaceDialog.py
index 5b474843d5..da682a6fc0 100644
--- a/plugins/3MFReader/WorkspaceDialog.py
+++ b/plugins/3MFReader/WorkspaceDialog.py
@@ -49,10 +49,10 @@ class WorkspaceDialog(QObject):
self._material_labels = []
self._extruders = []
self._objects_on_plate = False
+ self._is_printer_group = False
machineConflictChanged = pyqtSignal()
qualityChangesConflictChanged = pyqtSignal()
- definitionChangesConflictChanged = pyqtSignal()
materialConflictChanged = pyqtSignal()
numVisibleSettingsChanged = pyqtSignal()
activeModeChanged = pyqtSignal()
@@ -67,6 +67,16 @@ class WorkspaceDialog(QObject):
machineTypeChanged = pyqtSignal()
variantTypeChanged = pyqtSignal()
extrudersChanged = pyqtSignal()
+ isPrinterGroupChanged = pyqtSignal()
+
+ @pyqtProperty(bool, notify = isPrinterGroupChanged)
+ def isPrinterGroup(self) -> bool:
+ return self._is_printer_group
+
+ def setIsPrinterGroup(self, value: bool):
+ if value != self._is_printer_group:
+ self._is_printer_group = value
+ self.isPrinterGroupChanged.emit()
@pyqtProperty(str, notify=variantTypeChanged)
def variantType(self):
@@ -196,10 +206,6 @@ class WorkspaceDialog(QObject):
def qualityChangesConflict(self):
return self._has_quality_changes_conflict
- @pyqtProperty(bool, notify=definitionChangesConflictChanged)
- def definitionChangesConflict(self):
- return self._has_definition_changes_conflict
-
@pyqtProperty(bool, notify=materialConflictChanged)
def materialConflict(self):
return self._has_material_conflict
@@ -229,18 +235,11 @@ class WorkspaceDialog(QObject):
self._has_quality_changes_conflict = quality_changes_conflict
self.qualityChangesConflictChanged.emit()
- def setDefinitionChangesConflict(self, definition_changes_conflict):
- if self._has_definition_changes_conflict != definition_changes_conflict:
- self._has_definition_changes_conflict = definition_changes_conflict
- self.definitionChangesConflictChanged.emit()
-
def getResult(self):
if "machine" in self._result and not self._has_machine_conflict:
self._result["machine"] = None
if "quality_changes" in self._result and not self._has_quality_changes_conflict:
self._result["quality_changes"] = None
- if "definition_changes" in self._result and not self._has_definition_changes_conflict:
- self._result["definition_changes"] = None
if "material" in self._result and not self._has_material_conflict:
self._result["material"] = None
diff --git a/plugins/3MFReader/WorkspaceDialog.qml b/plugins/3MFReader/WorkspaceDialog.qml
index 5418dcef6d..58d881c915 100644
--- a/plugins/3MFReader/WorkspaceDialog.qml
+++ b/plugins/3MFReader/WorkspaceDialog.qml
@@ -108,7 +108,22 @@ UM.Dialog
text: catalog.i18nc("@info:tooltip", "How should the conflict in the machine be resolved?")
ComboBox
{
- model: resolveStrategiesModel
+ model: ListModel
+ {
+ Component.onCompleted:
+ {
+ append({"key": "override", "label": catalog.i18nc("@action:ComboBox option", "Update") + " " + manager.machineName});
+ append({"key": "new", "label": catalog.i18nc("@action:ComboBox option", "Create new")});
+ }
+ }
+ Connections
+ {
+ target: manager
+ onMachineNameChanged:
+ {
+ machineResolveComboBox.model.get(0).label = catalog.i18nc("@action:ComboBox option", "Update") + " " + manager.machineName;
+ }
+ }
textRole: "label"
id: machineResolveComboBox
width: parent.width
@@ -141,7 +156,7 @@ UM.Dialog
height: childrenRect.height
Label
{
- text: catalog.i18nc("@action:label", "Name")
+ text: catalog.i18nc("@action:label", manager.isPrinterGroup ? "Printer Group" : "Printer Name")
width: (parent.width / 3) | 0
}
Label
diff --git a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py
index 825259ad58..3f5e69317e 100644
--- a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py
+++ b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py
@@ -1,14 +1,15 @@
-# Copyright (c) 2017 Ultimaker B.V.
+# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
-from UM.Workspace.WorkspaceWriter import WorkspaceWriter
+import configparser
+from io import StringIO
+import zipfile
+
from UM.Application import Application
+from UM.Logger import Logger
from UM.Preferences import Preferences
from UM.Settings.ContainerRegistry import ContainerRegistry
-from cura.Settings.ExtruderManager import ExtruderManager
-import zipfile
-from io import StringIO
-import configparser
+from UM.Workspace.WorkspaceWriter import WorkspaceWriter
class ThreeMFWorkspaceWriter(WorkspaceWriter):
@@ -16,7 +17,10 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
super().__init__()
def write(self, stream, nodes, mode=WorkspaceWriter.OutputMode.BinaryMode):
- mesh_writer = Application.getInstance().getMeshFileHandler().getWriter("3MFWriter")
+ application = Application.getInstance()
+ machine_manager = application.getMachineManager()
+
+ mesh_writer = application.getMeshFileHandler().getWriter("3MFWriter")
if not mesh_writer: # We need to have the 3mf mesh writer, otherwise we can't save the entire workspace
return False
@@ -29,17 +33,17 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
if archive is None: # This happens if there was no mesh data to write.
archive = zipfile.ZipFile(stream, "w", compression = zipfile.ZIP_DEFLATED)
- global_container_stack = Application.getInstance().getGlobalContainerStack()
+ global_stack = machine_manager.activeMachine
# Add global container stack data to the archive.
- self._writeContainerToArchive(global_container_stack, archive)
+ self._writeContainerToArchive(global_stack, archive)
# Also write all containers in the stack to the file
- for container in global_container_stack.getContainers():
+ for container in global_stack.getContainers():
self._writeContainerToArchive(container, archive)
# Check if the machine has extruders and save all that data as well.
- for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(global_container_stack.getId()):
+ for extruder_stack in global_stack.extruders.values():
self._writeContainerToArchive(extruder_stack, archive)
for container in extruder_stack.getContainers():
self._writeContainerToArchive(container, archive)
@@ -57,11 +61,11 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
# Save Cura version
version_file = zipfile.ZipInfo("Cura/version.ini")
- version_config_parser = configparser.ConfigParser()
+ version_config_parser = configparser.ConfigParser(interpolation = None)
version_config_parser.add_section("versions")
- version_config_parser.set("versions", "cura_version", Application.getInstance().getVersion())
- version_config_parser.set("versions", "build_type", Application.getInstance().getBuildType())
- version_config_parser.set("versions", "is_debug_mode", str(Application.getInstance().getIsDebugMode()))
+ version_config_parser.set("versions", "cura_version", application.getVersion())
+ version_config_parser.set("versions", "build_type", application.getBuildType())
+ version_config_parser.set("versions", "is_debug_mode", str(application.getIsDebugMode()))
version_file_string = StringIO()
version_config_parser.write(version_file_string)
@@ -85,7 +89,8 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
# Some containers have a base file, which should then be the file to use.
if "base_file" in container.getMetaData():
base_file = container.getMetaDataEntry("base_file")
- container = ContainerRegistry.getInstance().findContainers(id = base_file)[0]
+ if base_file != container.getId():
+ container = ContainerRegistry.getInstance().findContainers(id = base_file)[0]
file_name = "Cura/%s.%s" % (container.getId(), file_suffix)
diff --git a/plugins/3MFWriter/ThreeMFWriter.py b/plugins/3MFWriter/ThreeMFWriter.py
index c4b7035cf1..ff6333763a 100644
--- a/plugins/3MFWriter/ThreeMFWriter.py
+++ b/plugins/3MFWriter/ThreeMFWriter.py
@@ -68,7 +68,7 @@ class ThreeMFWriter(MeshWriter):
if not isinstance(um_node, SceneNode):
return None
- active_build_plate_nr = CuraApplication.getInstance().getBuildPlateModel().activeBuildPlate
+ active_build_plate_nr = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
if um_node.callDecoration("getBuildPlateNumber") != active_build_plate_nr:
return
diff --git a/plugins/AutoSave/AutoSave.py b/plugins/AutoSave/AutoSave.py
index 331f328f2d..5fdac502b5 100644
--- a/plugins/AutoSave/AutoSave.py
+++ b/plugins/AutoSave/AutoSave.py
@@ -9,6 +9,7 @@ from UM.Application import Application
from UM.Resources import Resources
from UM.Logger import Logger
+
class AutoSave(Extension):
def __init__(self):
super().__init__()
@@ -16,18 +17,40 @@ class AutoSave(Extension):
Preferences.getInstance().preferenceChanged.connect(self._triggerTimer)
self._global_stack = None
- Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
- self._onGlobalStackChanged()
Preferences.getInstance().addPreference("cura/autosave_delay", 1000 * 10)
self._change_timer = QTimer()
self._change_timer.setInterval(Preferences.getInstance().getValue("cura/autosave_delay"))
self._change_timer.setSingleShot(True)
- self._change_timer.timeout.connect(self._onTimeout)
self._saving = False
+ # At this point, the Application instance has not finished its constructor call yet, so directly using something
+ # like Application.getInstance() is not correct. The initialisation now will only gets triggered after the
+ # application finishes its start up successfully.
+ self._init_timer = QTimer()
+ self._init_timer.setInterval(1000)
+ self._init_timer.setSingleShot(True)
+ self._init_timer.timeout.connect(self.initialize)
+ self._init_timer.start()
+
+ def initialize(self):
+ # only initialise if the application is created and has started
+ from cura.CuraApplication import CuraApplication
+ if not CuraApplication.Created:
+ self._init_timer.start()
+ return
+ if not CuraApplication.getInstance().started:
+ self._init_timer.start()
+ return
+
+ self._change_timer.timeout.connect(self._onTimeout)
+ Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
+ self._onGlobalStackChanged()
+
+ self._triggerTimer()
+
def _triggerTimer(self, *args):
if not self._saving:
self._change_timer.start()
diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt
index 6b394f1e2e..8a031c9eae 100755
--- a/plugins/ChangeLogPlugin/ChangeLog.txt
+++ b/plugins/ChangeLogPlugin/ChangeLog.txt
@@ -1,3 +1,9 @@
+[3.2.1]
+*Bug fixes
+- Fixed issues where Cura crashes on startup and loading profiles
+- Updated translations
+- Fixed an issue where the text would not render properly
+
[3.2.0]
*Tree support
Experimental tree-like support structure that uses ‘branches’ to support prints. Branches ‘grow’ and multiply towards the model, with fewer contact points than alternative support methods. This results in better surface finishes for organic-shaped prints.
diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py
index 8d3b223c2c..9873d91c05 100755
--- a/plugins/CuraEngineBackend/CuraEngineBackend.py
+++ b/plugins/CuraEngineBackend/CuraEngineBackend.py
@@ -10,7 +10,6 @@ from UM.Logger import Logger
from UM.Message import Message
from UM.PluginRegistry import PluginRegistry
from UM.Resources import Resources
-from UM.Settings.Validator import ValidatorState #To find if a setting is in an error state. We can't slice then.
from UM.Platform import Platform
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Qt.Duration import DurationFormat
@@ -32,7 +31,11 @@ import Arcus
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
+
class CuraEngineBackend(QObject, Backend):
+
+ backendError = Signal()
+
## Starts the back-end plug-in.
#
# This registers all the signal listeners and prepares for communication
@@ -59,23 +62,26 @@ class CuraEngineBackend(QObject, Backend):
default_engine_location = execpath
break
+ self._application = Application.getInstance()
+ self._multi_build_plate_model = None
+ self._machine_error_checker = None
+
if not default_engine_location:
raise EnvironmentError("Could not find CuraEngine")
- Logger.log("i", "Found CuraEngine at: %s" %(default_engine_location))
+ Logger.log("i", "Found CuraEngine at: %s", default_engine_location)
default_engine_location = os.path.abspath(default_engine_location)
Preferences.getInstance().addPreference("backend/location", default_engine_location)
# Workaround to disable layer view processing if layer view is not active.
self._layer_view_active = False
- Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
- Application.getInstance().getBuildPlateModel().activeBuildPlateChanged.connect(self._onActiveViewChanged)
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._scene = Application.getInstance().getController().getScene()
+ self._scene = self._application.getController().getScene()
self._scene.sceneChanged.connect(self._onSceneChanged)
# Triggers for auto-slicing. Auto-slicing is triggered as follows:
@@ -83,19 +89,10 @@ class CuraEngineBackend(QObject, Backend):
# - whenever there is a value change, we start the timer
# - sometimes an error check can get scheduled for a value change, in that case, we ONLY want to start the
# auto-slicing timer when that error check is finished
- # If there is an error check, it will set the "_is_error_check_scheduled" flag, stop the auto-slicing timer,
- # and only wait for the error check to be finished to start the auto-slicing timer again.
+ # 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
- Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
- Application.getInstance().getExtruderManager().extrudersAdded.connect(self._onGlobalStackChanged)
- self._onGlobalStackChanged()
-
- Application.getInstance().stacksValidationFinished.connect(self._onStackErrorCheckFinished)
-
- # A flag indicating if an error check was scheduled
- # If so, we will stop the auto-slice timer and start upon the error check
- self._is_error_check_scheduled = False
# Listeners for receiving messages from the back-end.
self._message_handlers["cura.proto.Layer"] = self._onLayerMessage
@@ -121,14 +118,8 @@ class CuraEngineBackend(QObject, Backend):
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.backendQuit.connect(self._onBackendQuit)
- self.backendConnected.connect(self._onBackendConnected)
-
- # When a tool operation is in progress, don't slice. So we need to listen for tool operations.
- Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted)
- Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped)
-
self._slice_start_time = None
+ self._is_disabled = False
Preferences.getInstance().addPreference("general/auto_slice", True)
@@ -142,6 +133,30 @@ class CuraEngineBackend(QObject, Backend):
self.determineAutoSlicing()
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
+ self._application.initializationFinished.connect(self.initialize)
+
+ def initialize(self):
+ self._multi_build_plate_model = self._application.getMultiBuildPlateModel()
+
+ self._application.getController().activeViewChanged.connect(self._onActiveViewChanged)
+ self._multi_build_plate_model.activeBuildPlateChanged.connect(self._onActiveViewChanged)
+
+ self._application.globalContainerStackChanged.connect(self._onGlobalStackChanged)
+ self._onGlobalStackChanged()
+
+ # extruder enable / disable. Actually wanted to use machine manager here, but the initialization order causes it to crash
+ ExtruderManager.getInstance().extrudersChanged.connect(self._extruderChanged)
+
+ self.backendQuit.connect(self._onBackendQuit)
+ self.backendConnected.connect(self._onBackendConnected)
+
+ # When a tool operation is in progress, don't slice. So we need to listen for tool operations.
+ self._application.getController().toolOperationStarted.connect(self._onToolOperationStarted)
+ self._application.getController().toolOperationStopped.connect(self._onToolOperationStopped)
+
+ self._machine_error_checker = self._application.getMachineErrorChecker()
+ self._machine_error_checker.errorCheckFinished.connect(self._onStackErrorCheckFinished)
+
## Terminate the engine process.
#
# This function should terminate the engine process.
@@ -187,14 +202,12 @@ class CuraEngineBackend(QObject, Backend):
## Manually triggers a reslice
@pyqtSlot()
def forceSlice(self):
- if self._use_timer:
- self._change_timer.start()
- else:
- self.slice()
+ self.markSliceAll()
+ self.slice()
## Perform a slice of the scene.
def slice(self):
- Logger.log("d", "starting to slice!")
+ Logger.log("d", "Starting to slice...")
self._slice_start_time = time()
if not self._build_plates_to_be_sliced:
self.processingProgress.emit(1.0)
@@ -202,17 +215,21 @@ class CuraEngineBackend(QObject, Backend):
return
if self._process_layers_job:
- Logger.log("d", " ## Process layers job still busy, trying later")
+ Logger.log("d", "Process layers job still busy, trying later.")
return
if not hasattr(self._scene, "gcode_dict"):
self._scene.gcode_dict = {}
# see if we really have to slice
- active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
+ active_build_plate = Application.getInstance().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._numObjects()
+ num_objects = self._numObjectsPerBuildPlate()
+
+ self._stored_layer_data = []
+ 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] = []
Logger.log("d", "Build plate %s has no objects to be sliced, skipping", build_plate_to_be_sliced)
@@ -220,9 +237,6 @@ class CuraEngineBackend(QObject, Backend):
self.slice()
return
- self._stored_layer_data = []
- self._stored_optimized_layer_data[build_plate_to_be_sliced] = []
-
if Application.getInstance().getPrintInformation() and build_plate_to_be_sliced == active_build_plate:
Application.getInstance().getPrintInformation().setToZeroPrintInformation(build_plate_to_be_sliced)
@@ -292,6 +306,7 @@ class CuraEngineBackend(QObject, Backend):
if job.isCancelled() or job.getError() or job.getResult() == StartSliceJob.StartJobResult.Error:
self.backendStateChange.emit(BackendState.Error)
+ self.backendError.emit(job)
return
if job.getResult() == StartSliceJob.StartJobResult.MaterialIncompatible:
@@ -300,6 +315,7 @@ class CuraEngineBackend(QObject, Backend):
"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()
self.backendStateChange.emit(BackendState.Error)
+ self.backendError.emit(job)
else:
self.backendStateChange.emit(BackendState.NotStarted)
return
@@ -328,6 +344,7 @@ class CuraEngineBackend(QObject, Backend):
title = catalog.i18nc("@info:title", "Unable to slice"))
self._error_message.show()
self.backendStateChange.emit(BackendState.Error)
+ self.backendError.emit(job)
else:
self.backendStateChange.emit(BackendState.NotStarted)
return
@@ -350,6 +367,7 @@ class CuraEngineBackend(QObject, Backend):
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:
@@ -358,6 +376,7 @@ class CuraEngineBackend(QObject, Backend):
title = catalog.i18nc("@info:title", "Unable to slice"))
self._error_message.show()
self.backendStateChange.emit(BackendState.Error)
+ self.backendError.emit(job)
else:
self.backendStateChange.emit(BackendState.NotStarted)
@@ -367,9 +386,9 @@ class CuraEngineBackend(QObject, Backend):
title = catalog.i18nc("@info:title", "Unable to slice"))
self._error_message.show()
self.backendStateChange.emit(BackendState.Error)
+ self.backendError.emit(job)
else:
self.backendStateChange.emit(BackendState.NotStarted)
- pass
self._invokeSlice()
return
@@ -387,6 +406,7 @@ class CuraEngineBackend(QObject, Backend):
# - decorator isBlockSlicing is found (used in g-code reader)
def determineAutoSlicing(self):
enable_timer = True
+ self._is_disabled = False
if not Preferences.getInstance().getValue("general/auto_slice"):
enable_timer = False
@@ -394,6 +414,7 @@ class CuraEngineBackend(QObject, Backend):
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
@@ -409,7 +430,7 @@ class CuraEngineBackend(QObject, Backend):
return False
## Return a dict with number of objects per build plate
- def _numObjects(self):
+ def _numObjectsPerBuildPlate(self):
num_objects = defaultdict(int)
for node in DepthFirstIterator(self._scene.getRoot()):
# Only count sliceable objects
@@ -436,7 +457,7 @@ class CuraEngineBackend(QObject, Backend):
source_build_plate_number = source.callDecoration("getBuildPlateNumber")
if source == self._scene.getRoot():
# we got the root node
- num_objects = self._numObjects()
+ num_objects = self._numObjectsPerBuildPlate()
for build_plate_number in list(self._last_num_objects.keys()) + list(num_objects.keys()):
if build_plate_number not in self._last_num_objects or num_objects[build_plate_number] != self._last_num_objects[build_plate_number]:
self._last_num_objects[build_plate_number] = num_objects[build_plate_number]
@@ -500,7 +521,7 @@ class CuraEngineBackend(QObject, Backend):
node.getParent().removeChild(node)
def markSliceAll(self):
- for build_plate_number in range(Application.getInstance().getBuildPlateModel().maxBuildPlate + 1):
+ for build_plate_number in range(Application.getInstance().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)
@@ -524,11 +545,13 @@ class CuraEngineBackend(QObject, Backend):
elif property == "validationState":
if self._use_timer:
- self._is_error_check_scheduled = True
self._change_timer.stop()
def _onStackErrorCheckFinished(self):
- self._is_error_check_scheduled = False
+ self.determineAutoSlicing()
+ if self._is_disabled:
+ return
+
if not self._slicing and self._build_plates_to_be_sliced:
self.needsSlicing()
self._onChanged()
@@ -543,6 +566,8 @@ class CuraEngineBackend(QObject, Backend):
#
# \param message The protobuf message containing sliced layer data.
def _onOptimizedLayerMessage(self, message):
+ 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)
## Called when a progress message is received from the engine.
@@ -552,12 +577,15 @@ class CuraEngineBackend(QObject, Backend):
self.processingProgress.emit(message.amount)
self.backendStateChange.emit(BackendState.Processing)
- # testing
def _invokeSlice(self):
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
- if self._is_error_check_scheduled:
+ if self._machine_error_checker is None:
+ self._change_timer.stop()
+ return
+
+ if self._machine_error_checker.needToWaitForResult:
self._change_timer.stop()
else:
self._change_timer.start()
@@ -583,8 +611,13 @@ class CuraEngineBackend(QObject, Backend):
Logger.log("d", "Slicing took %s seconds", time() - self._slice_start_time )
# See if we need to process the sliced layers job.
- active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
- if self._layer_view_active and (self._process_layers_job is None or not self._process_layers_job.isRunning()) and active_build_plate == self._start_slice_job_build_plate:
+ active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
+ if (
+ self._layer_view_active and
+ (self._process_layers_job is None or not self._process_layers_job.isRunning()) and
+ active_build_plate == self._start_slice_job_build_plate and
+ active_build_plate not in self._build_plates_to_be_sliced):
+
self._startProcessSlicedLayersJob(active_build_plate)
# self._onActiveViewChanged()
self._start_slice_job_build_plate = None
@@ -623,7 +656,11 @@ class CuraEngineBackend(QObject, Backend):
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
- if self._is_error_check_scheduled:
+ if self._machine_error_checker is None:
+ self._change_timer.stop()
+ return
+
+ if self._machine_error_checker.needToWaitForResult:
self._change_timer.stop()
else:
self._change_timer.start()
@@ -703,13 +740,17 @@ class CuraEngineBackend(QObject, Backend):
application = Application.getInstance()
view = application.getController().getActiveView()
if view:
- active_build_plate = application.getBuildPlateModel().activeBuildPlate
+ active_build_plate = 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
# if we are slicing, there is no need to re-calculate the data as it will be invalid in a moment.
# TODO: what build plate I am slicing
- if active_build_plate in self._stored_optimized_layer_data and not self._slicing and not self._process_layers_job:
+ if (active_build_plate in self._stored_optimized_layer_data and
+ not self._slicing and
+ not self._process_layers_job and
+ active_build_plate not in self._build_plates_to_be_sliced):
+
self._startProcessSlicedLayersJob(active_build_plate)
else:
self._layer_view_active = False
@@ -775,3 +816,9 @@ class CuraEngineBackend(QObject, Backend):
def tickle(self):
if self._use_timer:
self._change_timer.start()
+
+ def _extruderChanged(self):
+ 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)
+ self._invokeSlice()
diff --git a/plugins/CuraEngineBackend/ProcessGCodeJob.py b/plugins/CuraEngineBackend/ProcessGCodeJob.py
index ed430f8fa9..817daa9f85 100644
--- a/plugins/CuraEngineBackend/ProcessGCodeJob.py
+++ b/plugins/CuraEngineBackend/ProcessGCodeJob.py
@@ -12,6 +12,6 @@ class ProcessGCodeLayerJob(Job):
self._message = message
def run(self):
- active_build_plate_id = Application.getInstance().getBuildPlateModel().activeBuildPlate
+ active_build_plate_id = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
gcode_list = self._scene.gcode_dict[active_build_plate_id]
gcode_list.append(self._message.data.decode("utf-8", "replace"))
diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py
index c1fc597d80..cbc097bb33 100644
--- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py
+++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py
@@ -81,7 +81,8 @@ class ProcessSlicedLayersJob(Job):
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
- new_node = CuraSceneNode()
+ # The no_setting_override is here because adding the SettingOverrideDecorator will trigger a reslice
+ new_node = CuraSceneNode(no_setting_override = True)
new_node.addDecorator(BuildPlateDecorator(self._build_plate_number))
# Force garbage collection.
diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py
index d4a958ae5f..0297a34385 100644
--- a/plugins/CuraEngineBackend/StartSliceJob.py
+++ b/plugins/CuraEngineBackend/StartSliceJob.py
@@ -5,6 +5,7 @@ import numpy
from string import Formatter
from enum import IntEnum
import time
+import re
from UM.Job import Job
from UM.Application import Application
@@ -54,19 +55,19 @@ class GcodeStartEndFormatter(Formatter):
extruder_nr = int(kwargs["-1"][key_fragments[1]]) # get extruder_nr values from the global stack
except (KeyError, ValueError):
# either the key does not exist, or the value is not an int
- Logger.log("w", "Unable to determine stack nr '%s' for key '%s' in start/end gcode, using global stack", key_fragments[1], key_fragments[0])
+ Logger.log("w", "Unable to determine stack nr '%s' for key '%s' in start/end g-code, using global stack", key_fragments[1], key_fragments[0])
elif len(key_fragments) != 1:
- Logger.log("w", "Incorrectly formatted placeholder '%s' in start/end gcode", key)
+ Logger.log("w", "Incorrectly formatted placeholder '%s' in start/end g-code", key)
return "{" + str(key) + "}"
key = key_fragments[0]
try:
return kwargs[str(extruder_nr)][key]
except KeyError:
- Logger.log("w", "Unable to replace '%s' placeholder in start/end gcode", key)
+ Logger.log("w", "Unable to replace '%s' placeholder in start/end g-code", key)
return "{" + key + "}"
else:
- Logger.log("w", "Incorrectly formatted placeholder '%s' in start/end gcode", key)
+ Logger.log("w", "Incorrectly formatted placeholder '%s' in start/end g-code", key)
return "{" + str(key) + "}"
@@ -128,16 +129,19 @@ class StartSliceJob(Job):
self.setResult(StartJobResult.MaterialIncompatible)
return
- for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
+ for position, extruder_stack in stack.extruders.items():
material = extruder_stack.findContainer({"type": "material"})
+ if not extruder_stack.isEnabled:
+ continue
if material:
if material.getMetaDataEntry("compatible") == False:
self.setResult(StartJobResult.MaterialIncompatible)
return
+
# Don't slice if there is a per object setting with an error value.
for node in DepthFirstIterator(self._scene.getRoot()):
- if type(node) is not CuraSceneNode or not node.isSelectable():
+ if not isinstance(node, CuraSceneNode) or not node.isSelectable():
continue
if self._checkStackForErrors(node.callDecoration("getStack")):
@@ -169,7 +173,7 @@ class StartSliceJob(Job):
children = node.getAllChildren()
children.append(node)
for child_node in children:
- if type(child_node) is CuraSceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None:
+ if child_node.getMeshData() and child_node.getMeshData().getVertices() is not None:
temp_list.append(child_node)
if temp_list:
@@ -181,17 +185,21 @@ class StartSliceJob(Job):
temp_list = []
has_printing_mesh = False
for node in DepthFirstIterator(self._scene.getRoot()):
- if node.callDecoration("isSliceable") and type(node) is CuraSceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None:
+ 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
if per_object_stack:
is_non_printing_mesh = any(per_object_stack.getProperty(key, "value") for key in NON_PRINTING_MESH_SETTINGS)
- if (node.callDecoration("getBuildPlateNumber") == self._build_plate_number):
- if not getattr(node, "_outside_buildarea", False) or is_non_printing_mesh:
- temp_list.append(node)
- if not is_non_printing_mesh:
- has_printing_mesh = True
+ # Find a reason not to add the node
+ if node.callDecoration("getBuildPlateNumber") != self._build_plate_number:
+ continue
+ if getattr(node, "_outside_buildarea", False) and not is_non_printing_mesh:
+ continue
+
+ temp_list.append(node)
+ if not is_non_printing_mesh:
+ has_printing_mesh = True
Job.yieldThread()
@@ -203,10 +211,22 @@ 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()}
+ filtered_object_groups = []
+ for group in object_groups:
+ stack = Application.getInstance().getGlobalContainerStack()
+ skip_group = False
+ for node in group:
+ if not extruders_enabled[node.callDecoration("getActiveExtruderPosition")]:
+ skip_group = True
+ break
+ if not skip_group:
+ filtered_object_groups.append(group)
+
# There are cases when there is nothing to slice. This can happen due to one at a time slicing not being
# able to find a possible sequence or because there are no objects on the build plate (or they are outside
# the build volume)
- if not object_groups:
+ if not filtered_object_groups:
self.setResult(StartJobResult.NothingToSlice)
return
@@ -217,9 +237,9 @@ class StartSliceJob(Job):
for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
self._buildExtruderMessage(extruder_stack)
- for group in object_groups:
+ for group in filtered_object_groups:
group_message = self._slice_message.addRepeatedMessage("object_lists")
- if group[0].getParent().callDecoration("isGroup"):
+ if group[0].getParent() is not None and group[0].getParent().callDecoration("isGroup"):
self._handlePerObjectSettings(group[0].getParent(), group_message)
for object in group:
mesh_data = object.getMeshData()
@@ -267,9 +287,15 @@ class StartSliceJob(Job):
# \return A dictionary of replacement tokens to the values they should be
# replaced with.
def _buildReplacementTokens(self, stack) -> dict:
+ default_extruder_position = int(Application.getInstance().getMachineManager().defaultExtruderPosition)
result = {}
for key in stack.getAllKeys():
- result[key] = stack.getProperty(key, "value")
+ setting_type = stack.definition.getProperty(key, "type")
+ value = stack.getProperty(key, "value")
+ if setting_type == "extruder" and value == -1:
+ # replace with the default value
+ value = default_extruder_position
+ result[key] = value
Job.yieldThread()
result["print_bed_temperature"] = result["material_bed_temperature"] # Renamed settings.
@@ -307,7 +333,7 @@ class StartSliceJob(Job):
settings["default_extruder_nr"] = default_extruder_nr
return str(fmt.format(value, **settings))
except:
- Logger.logException("w", "Unable to do token replacement on start/end gcode")
+ Logger.logException("w", "Unable to do token replacement on start/end g-code")
return str(value)
## Create extruder message from stack
@@ -343,10 +369,12 @@ class StartSliceJob(Job):
# Pre-compute material material_bed_temp_prepend and material_print_temp_prepend
start_gcode = settings["machine_start_gcode"]
- bed_temperature_settings = {"material_bed_temperature", "material_bed_temperature_layer_0"}
- settings["material_bed_temp_prepend"] = all(("{" + setting + "}" not in start_gcode for setting in bed_temperature_settings))
- print_temperature_settings = {"material_print_temperature", "material_print_temperature_layer_0", "default_material_print_temperature", "material_initial_print_temperature", "material_final_print_temperature", "material_standby_temperature"}
- settings["material_print_temp_prepend"] = all(("{" + setting + "}" not in start_gcode for setting in print_temperature_settings))
+ bed_temperature_settings = ["material_bed_temperature", "material_bed_temperature_layer_0"]
+ pattern = r"\{(%s)(,\s?\w+)?\}" % "|".join(bed_temperature_settings) # match {setting} as well as {setting, extruder_nr}
+ settings["material_bed_temp_prepend"] = re.search(pattern, start_gcode) == None
+ print_temperature_settings = ["material_print_temperature", "material_print_temperature_layer_0", "default_material_print_temperature", "material_initial_print_temperature", "material_final_print_temperature", "material_standby_temperature"]
+ pattern = r"\{(%s)(,\s?\w+)?\}" % "|".join(print_temperature_settings) # match {setting} as well as {setting, extruder_nr}
+ settings["material_print_temp_prepend"] = re.search(pattern, start_gcode) == None
# 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
@@ -373,11 +401,11 @@ class StartSliceJob(Job):
# limit_to_extruder property.
def _buildGlobalInheritsStackMessage(self, stack):
for key in stack.getAllKeys():
- extruder = int(round(float(stack.getProperty(key, "limit_to_extruder"))))
- if extruder >= 0: #Set to a specific extruder.
+ extruder_position = int(round(float(stack.getProperty(key, "limit_to_extruder"))))
+ if extruder_position >= 0: # Set to a specific extruder.
setting_extruder = self._slice_message.addRepeatedMessage("limit_to_extruder")
setting_extruder.name = key
- setting_extruder.extruder = extruder
+ setting_extruder.extruder = extruder_position
Job.yieldThread()
## Check if a node has per object settings and ensure that they are set correctly in the message
diff --git a/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py b/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py
index 8e4c70517f..23a040f2e2 100644
--- a/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py
+++ b/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py
@@ -20,7 +20,7 @@ i18n_catalog = i18nCatalog("cura")
# The plugin is currently only usable for applications maintained by Ultimaker. But it should be relatively easy
# to change it to work for other applications.
class FirmwareUpdateChecker(Extension):
- JEDI_VERSION_URL = "http://software.ultimaker.com/jedi/releases/latest.version"
+ JEDI_VERSION_URL = "http://software.ultimaker.com/jedi/releases/latest.version?utm_source=cura&utm_medium=software&utm_campaign=resources"
def __init__(self):
super().__init__()
diff --git a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py
index fd6c4680e8..66ee43209f 100644
--- a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py
+++ b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py
@@ -69,7 +69,7 @@ class FirmwareUpdateCheckerJob(Job):
# If we do this in a cool way, the download url should be available in the JSON file
if self._set_download_url_callback:
- self._set_download_url_callback("https://ultimaker.com/en/resources/20500-upgrade-firmware")
+ self._set_download_url_callback("https://ultimaker.com/en/resources/23129-updating-the-firmware?utm_source=cura&utm_medium=software&utm_campaign=hw-update")
message.actionTriggered.connect(self._callback)
message.show()
diff --git a/plugins/GCodeGzReader/GCodeGzReader.py b/plugins/GCodeGzReader/GCodeGzReader.py
new file mode 100644
index 0000000000..9d671a70bf
--- /dev/null
+++ b/plugins/GCodeGzReader/GCodeGzReader.py
@@ -0,0 +1,33 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+import gzip
+import tempfile
+
+from io import StringIO, BufferedIOBase #To write the g-code to a temporary buffer, and for typing.
+from typing import List
+
+from UM.Logger import Logger
+from UM.Mesh.MeshReader import MeshReader #The class we're extending/implementing.
+from UM.PluginRegistry import PluginRegistry
+from UM.Scene.SceneNode import SceneNode #For typing.
+
+## A file reader that reads gzipped g-code.
+#
+# If you're zipping g-code, you might as well use gzip!
+class GCodeGzReader(MeshReader):
+
+ def __init__(self):
+ super().__init__()
+ self._supported_extensions = [".gcode.gz"]
+
+ def read(self, file_name):
+ with open(file_name, "rb") as file:
+ file_data = file.read()
+ uncompressed_gcode = gzip.decompress(file_data)
+ with tempfile.NamedTemporaryFile() as temp_file:
+ temp_file.write(uncompressed_gcode)
+ PluginRegistry.getInstance().getPluginObject("GCodeReader").preRead(temp_file.name)
+ result = PluginRegistry.getInstance().getPluginObject("GCodeReader").read(temp_file.name)
+
+ return result
diff --git a/plugins/GCodeGzReader/__init__.py b/plugins/GCodeGzReader/__init__.py
new file mode 100644
index 0000000000..98965c00aa
--- /dev/null
+++ b/plugins/GCodeGzReader/__init__.py
@@ -0,0 +1,21 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from . import GCodeGzReader
+
+from UM.i18n import i18nCatalog
+i18n_catalog = i18nCatalog("cura")
+
+def getMetaData():
+ return {
+ "mesh_reader": [
+ {
+ "extension": "gcode.gz",
+ "description": i18n_catalog.i18nc("@item:inlistbox", "Compressed G-code File")
+ }
+ ]
+ }
+
+def register(app):
+ app.addNonSliceableExtension(".gcode.gz")
+ return { "mesh_reader": GCodeGzReader.GCodeGzReader() }
diff --git a/plugins/GCodeGzReader/plugin.json b/plugins/GCodeGzReader/plugin.json
new file mode 100644
index 0000000000..e9f14724e0
--- /dev/null
+++ b/plugins/GCodeGzReader/plugin.json
@@ -0,0 +1,8 @@
+{
+ "name": "Compressed G-code Reader",
+ "author": "Ultimaker B.V.",
+ "version": "1.0.0",
+ "description": "Reads g-code from a compressed archive.",
+ "api": 4,
+ "i18n-catalog": "cura"
+}
diff --git a/plugins/GCodeGzWriter/GCodeGzWriter.py b/plugins/GCodeGzWriter/GCodeGzWriter.py
new file mode 100644
index 0000000000..06fafb5995
--- /dev/null
+++ b/plugins/GCodeGzWriter/GCodeGzWriter.py
@@ -0,0 +1,41 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+import gzip
+from io import StringIO, BufferedIOBase #To write the g-code to a temporary buffer, and for typing.
+from typing import List
+
+from UM.Logger import Logger
+from UM.Mesh.MeshWriter import MeshWriter #The class we're extending/implementing.
+from UM.PluginRegistry import PluginRegistry
+from UM.Scene.SceneNode import SceneNode #For typing.
+
+## A file writer that writes gzipped g-code.
+#
+# If you're zipping g-code, you might as well use gzip!
+class GCodeGzWriter(MeshWriter):
+ ## Writes the gzipped g-code to a stream.
+ #
+ # Note that even though the function accepts a collection of nodes, the
+ # entire scene is always written to the file since it is not possible to
+ # separate the g-code for just specific nodes.
+ #
+ # \param stream The stream to write the gzipped g-code to.
+ # \param nodes This is ignored.
+ # \param mode Additional information on what type of stream to use. This
+ # must always be binary mode.
+ # \return Whether the write was successful.
+ def write(self, stream: BufferedIOBase, nodes: List[SceneNode], mode = MeshWriter.OutputMode.BinaryMode) -> bool:
+ if mode != MeshWriter.OutputMode.BinaryMode:
+ Logger.log("e", "GCodeGzWriter does not support text mode.")
+ return False
+
+ #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)
+ if not success: #Writing the g-code failed. Then I can also not write the gzipped g-code.
+ return False
+
+ result = gzip.compress(gcode_textio.getvalue().encode("utf-8"))
+ stream.write(result)
+ return True
diff --git a/plugins/GCodeGzWriter/__init__.py b/plugins/GCodeGzWriter/__init__.py
new file mode 100644
index 0000000000..c001467b3d
--- /dev/null
+++ b/plugins/GCodeGzWriter/__init__.py
@@ -0,0 +1,22 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from . import GCodeGzWriter
+
+from UM.i18n import i18nCatalog
+catalog = i18nCatalog("cura")
+
+def getMetaData():
+ return {
+ "mesh_writer": {
+ "output": [{
+ "extension": "gcode.gz",
+ "description": catalog.i18nc("@item:inlistbox", "Compressed G-code File"),
+ "mime_type": "application/gzip",
+ "mode": GCodeGzWriter.GCodeGzWriter.OutputMode.BinaryMode
+ }]
+ }
+ }
+
+def register(app):
+ return { "mesh_writer": GCodeGzWriter.GCodeGzWriter() }
diff --git a/plugins/GCodeGzWriter/plugin.json b/plugins/GCodeGzWriter/plugin.json
new file mode 100644
index 0000000000..9774e9a25c
--- /dev/null
+++ b/plugins/GCodeGzWriter/plugin.json
@@ -0,0 +1,8 @@
+{
+ "name": "Compressed G-code Writer",
+ "author": "Ultimaker B.V.",
+ "version": "1.0.0",
+ "description": "Writes g-code to a compressed archive.",
+ "api": 4,
+ "i18n-catalog": "cura"
+}
diff --git a/plugins/GCodeProfileReader/GCodeProfileReader.py b/plugins/GCodeProfileReader/GCodeProfileReader.py
index 9d7f0059f9..d6bda85a48 100644
--- a/plugins/GCodeProfileReader/GCodeProfileReader.py
+++ b/plugins/GCodeProfileReader/GCodeProfileReader.py
@@ -9,7 +9,7 @@ from UM.Logger import Logger
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
-from cura.ProfileReader import ProfileReader
+from cura.ProfileReader import ProfileReader, NoProfileException
## A class that reads profile data from g-code files.
#
@@ -66,12 +66,17 @@ class GCodeProfileReader(ProfileReader):
return None
serialized = unescapeGcodeComment(serialized)
+ serialized = serialized.strip()
+
+ if not serialized:
+ Logger.log("i", "No custom profile to import from this g-code: %s", file_name)
+ raise NoProfileException()
# serialized data can be invalid JSON
try:
json_data = json.loads(serialized)
except Exception as e:
- Logger.log("e", "Could not parse serialized JSON data from GCode %s, error: %s", file_name, e)
+ Logger.log("e", "Could not parse serialized JSON data from g-code %s, error: %s", file_name, e)
return None
profiles = []
diff --git a/plugins/GCodeProfileReader/plugin.json b/plugins/GCodeProfileReader/plugin.json
index 8111bc687c..f8f7d4c291 100644
--- a/plugins/GCodeProfileReader/plugin.json
+++ b/plugins/GCodeProfileReader/plugin.json
@@ -1,5 +1,5 @@
{
- "name": "GCode Profile Reader",
+ "name": "G-code Profile Reader",
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Provides support for importing profiles from g-code files.",
diff --git a/plugins/GCodeReader/FlavorParser.py b/plugins/GCodeReader/FlavorParser.py
index f63ba3ca69..c064ffbf10 100644
--- a/plugins/GCodeReader/FlavorParser.py
+++ b/plugins/GCodeReader/FlavorParser.py
@@ -437,7 +437,7 @@ 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().getBuildPlateModel().activeBuildPlate
+ active_build_plate_id = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
gcode_dict = {active_build_plate_id: gcode_list}
Application.getInstance().getController().getScene().gcode_dict = gcode_dict
diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py
index 2dfaf5aef7..ccd881afdc 100644
--- a/plugins/GCodeWriter/GCodeWriter.py
+++ b/plugins/GCodeWriter/GCodeWriter.py
@@ -1,16 +1,17 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
+import re # For escaping characters in the settings.
+import json
+import copy
+
from UM.Mesh.MeshWriter import MeshWriter
from UM.Logger import Logger
from UM.Application import Application
from UM.Settings.InstanceContainer import InstanceContainer
-from cura.Settings.ExtruderManager import ExtruderManager
+from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
-import re #For escaping characters in the settings.
-import json
-import copy
## Writes g-code to a file.
#
@@ -44,6 +45,8 @@ class GCodeWriter(MeshWriter):
def __init__(self):
super().__init__()
+ self._application = Application.getInstance()
+
## Writes the g-code for the entire scene to a stream.
#
# Note that even though the function accepts a collection of nodes, the
@@ -56,10 +59,10 @@ class GCodeWriter(MeshWriter):
# file. This must always be text mode.
def write(self, stream, nodes, mode = MeshWriter.OutputMode.TextMode):
if mode != MeshWriter.OutputMode.TextMode:
- Logger.log("e", "GCode Writer does not support non-text mode.")
+ Logger.log("e", "GCodeWriter does not support non-text mode.")
return False
- active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
+ active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
scene = Application.getInstance().getController().getScene()
gcode_dict = getattr(scene, "gcode_dict")
if not gcode_dict:
@@ -93,7 +96,6 @@ class GCodeWriter(MeshWriter):
return flat_container
-
## Serialises a container stack to prepare it for writing at the end of the
# g-code.
#
@@ -103,15 +105,20 @@ class GCodeWriter(MeshWriter):
# \param settings A container stack to serialise.
# \return A serialised string of the settings.
def _serialiseSettings(self, stack):
+ container_registry = self._application.getContainerRegistry()
+ quality_manager = self._application.getQualityManager()
+
prefix = ";SETTING_" + str(GCodeWriter.version) + " " # The prefix to put before each line.
prefix_length = len(prefix)
+ quality_type = stack.quality.getMetaDataEntry("quality_type")
container_with_profile = stack.qualityChanges
if container_with_profile.getId() == "empty_quality_changes":
- Logger.log("e", "No valid quality profile found, not writing settings to GCode!")
- return ""
+ # If the global quality changes is empty, create a new one
+ quality_name = container_registry.uniqueName(stack.quality.getName())
+ container_with_profile = quality_manager._createQualityChanges(quality_type, quality_name, stack, None)
- flat_global_container = self._createFlattenedContainerInstance(stack.getTop(), container_with_profile)
+ flat_global_container = self._createFlattenedContainerInstance(stack.userChanges, container_with_profile)
# If the quality changes is not set, we need to set type manually
if flat_global_container.getMetaDataEntry("type", None) is None:
flat_global_container.addMetaDataEntry("type", "quality_changes")
@@ -120,29 +127,47 @@ class GCodeWriter(MeshWriter):
if flat_global_container.getMetaDataEntry("quality_type", None) is None:
flat_global_container.addMetaDataEntry("quality_type", stack.quality.getMetaDataEntry("quality_type", "normal"))
+ # Get the machine definition ID for quality profiles
+ machine_definition_id_for_quality = getMachineDefinitionIDForQualitySearch(stack.definition)
+ flat_global_container.setMetaDataEntry("definition", machine_definition_id_for_quality)
+
serialized = flat_global_container.serialize()
data = {"global_quality": serialized}
- for extruder in sorted(stack.extruders.values(), key = lambda k: k.getMetaDataEntry("position")):
+ all_setting_keys = set(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":
- Logger.log("w", "No extruder quality profile found, not writing quality for extruder %s to file!", extruder.getId())
- continue
- flat_extruder_quality = self._createFlattenedContainerInstance(extruder.getTop(), extruder_quality)
+ # Same story, if quality changes is empty, create a new one
+ quality_name = container_registry.uniqueName(stack.quality.getName())
+ extruder_quality = quality_manager._createQualityChanges(quality_type, quality_name, stack, None)
+
+ flat_extruder_quality = self._createFlattenedContainerInstance(extruder.userChanges, extruder_quality)
# If the quality changes is not set, we need to set type manually
if flat_extruder_quality.getMetaDataEntry("type", None) is None:
flat_extruder_quality.addMetaDataEntry("type", "quality_changes")
# Ensure that extruder is set. (Can happen if we have empty quality changes).
- if flat_extruder_quality.getMetaDataEntry("extruder", None) is None:
- flat_extruder_quality.addMetaDataEntry("extruder", extruder.getBottom().getId())
+ if flat_extruder_quality.getMetaDataEntry("position", None) is None:
+ flat_extruder_quality.addMetaDataEntry("position", extruder.getMetaDataEntry("position"))
# Ensure that quality_type is set. (Can happen if we have empty quality changes).
if flat_extruder_quality.getMetaDataEntry("quality_type", None) is None:
flat_extruder_quality.addMetaDataEntry("quality_type", extruder.quality.getMetaDataEntry("quality_type", "normal"))
+
+ # Change the default definition
+ flat_extruder_quality.setMetaDataEntry("definition", machine_definition_id_for_quality)
+
extruder_serialized = flat_extruder_quality.serialize()
data.setdefault("extruder_quality", []).append(extruder_serialized)
+ all_setting_keys.update(set(flat_extruder_quality.getAllKeys()))
+
+ # Check if there is any profiles
+ if not all_setting_keys:
+ Logger.log("i", "No custom settings found, not writing settings to g-code.")
+ return ""
+
json_string = json.dumps(data)
# Escape characters that have a special meaning in g-code comments.
@@ -156,5 +181,5 @@ class GCodeWriter(MeshWriter):
# Lines have 80 characters, so the payload of each line is 80 - prefix.
for pos in range(0, len(escaped_string), 80 - prefix_length):
- result += prefix + escaped_string[pos : pos + 80 - prefix_length] + "\n"
+ result += prefix + escaped_string[pos: pos + 80 - prefix_length] + "\n"
return result
diff --git a/plugins/GCodeWriter/__init__.py b/plugins/GCodeWriter/__init__.py
index a89332a371..1a5728f510 100644
--- a/plugins/GCodeWriter/__init__.py
+++ b/plugins/GCodeWriter/__init__.py
@@ -13,7 +13,7 @@ def getMetaData():
"mesh_writer": {
"output": [{
"extension": "gcode",
- "description": catalog.i18nc("@item:inlistbox", "GCode File"),
+ "description": catalog.i18nc("@item:inlistbox", "G-code File"),
"mime_type": "text/x-gcode",
"mode": GCodeWriter.GCodeWriter.OutputMode.TextMode
}]
diff --git a/plugins/GCodeWriter/plugin.json b/plugins/GCodeWriter/plugin.json
index 5788b01375..5fcb1a3bd7 100644
--- a/plugins/GCodeWriter/plugin.json
+++ b/plugins/GCodeWriter/plugin.json
@@ -1,8 +1,8 @@
{
- "name": "GCode Writer",
+ "name": "G-code Writer",
"author": "Ultimaker B.V.",
"version": "1.0.0",
- "description": "Writes GCode to a file.",
+ "description": "Writes g-code to a file.",
"api": 4,
"i18n-catalog": "cura"
}
diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.py b/plugins/MachineSettingsAction/MachineSettingsAction.py
index baa0639d3f..ded59bf934 100755
--- a/plugins/MachineSettingsAction/MachineSettingsAction.py
+++ b/plugins/MachineSettingsAction/MachineSettingsAction.py
@@ -2,20 +2,16 @@
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import pyqtProperty, pyqtSignal
+
+import UM.i18n
from UM.FlameProfiler import pyqtSlot
-
-from cura.MachineAction import MachineAction
-
from UM.Application import Application
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.DefinitionContainer import DefinitionContainer
-from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
-from UM.Logger import Logger
-from cura.Settings.ExtruderManager import ExtruderManager
+from cura.MachineAction import MachineAction
from cura.Settings.CuraStackBuilder import CuraStackBuilder
-import UM.i18n
catalog = UM.i18n.i18nCatalog("cura")
@@ -26,6 +22,8 @@ class MachineSettingsAction(MachineAction):
super().__init__("MachineSettingsAction", catalog.i18nc("@action", "Machine Settings"))
self._qml_url = "MachineSettingsAction.qml"
+ self._application = Application.getInstance()
+
self._global_container_stack = None
from cura.Settings.CuraContainerStack import _ContainerIndexes
@@ -34,38 +32,44 @@ class MachineSettingsAction(MachineAction):
self._container_registry = ContainerRegistry.getInstance()
self._container_registry.containerAdded.connect(self._onContainerAdded)
self._container_registry.containerRemoved.connect(self._onContainerRemoved)
- Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
+ self._application.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
- self._empty_container = self._container_registry.getEmptyInstanceContainer()
+ self._backend = self._application.getBackend()
- self._backend = Application.getInstance().getBackend()
+ self._empty_definition_container_id_list = []
+
+ def _isEmptyDefinitionChanges(self, container_id: str):
+ if not self._empty_definition_container_id_list:
+ self._empty_definition_container_id_list = [self._application.empty_container.getId(),
+ self._application.empty_definition_changes_container.getId()]
+ return container_id in self._empty_definition_container_id_list
def _onContainerAdded(self, container):
# Add this action as a supported action to all machine definitions
if isinstance(container, DefinitionContainer) and container.getMetaDataEntry("type") == "machine":
- Application.getInstance().getMachineActionManager().addSupportedAction(container.getId(), self.getKey())
+ self._application.getMachineActionManager().addSupportedAction(container.getId(), self.getKey())
def _onContainerRemoved(self, container):
# Remove definition_changes containers when a stack is removed
if container.getMetaDataEntry("type") in ["machine", "extruder_train"]:
- definition_changes_container = container.definitionChanges
- if definition_changes_container == self._empty_container:
+ definition_changes_id = container.definitionChanges.getId()
+ if self._isEmptyDefinitionChanges(definition_changes_id):
return
- self._container_registry.removeContainer(definition_changes_container.getId())
+ self._container_registry.removeContainer(definition_changes_id)
def _reset(self):
if not self._global_container_stack:
return
# Make sure there is a definition_changes container to store the machine settings
- definition_changes_container = self._global_container_stack.definitionChanges
- if definition_changes_container == self._empty_container:
- definition_changes_container = CuraStackBuilder.createDefinitionChangesContainer(
- self._global_container_stack, self._global_container_stack.getName() + "_settings")
+ definition_changes_id = self._global_container_stack.definitionChanges.getId()
+ if self._isEmptyDefinitionChanges(definition_changes_id):
+ CuraStackBuilder.createDefinitionChangesContainer(self._global_container_stack,
+ self._global_container_stack.getName() + "_settings")
# Notify the UI in which container to store the machine settings data
- from cura.Settings.CuraContainerStack import CuraContainerStack, _ContainerIndexes
+ from cura.Settings.CuraContainerStack import _ContainerIndexes
container_index = _ContainerIndexes.DefinitionChanges
if container_index != self._container_index:
@@ -107,13 +111,13 @@ class MachineSettingsAction(MachineAction):
def setMachineExtruderCount(self, extruder_count):
# Note: this method was in this class before, but since it's quite generic and other plugins also need it
# it was moved to the machine manager instead. Now this method just calls the machine manager.
- Application.getInstance().getMachineManager().setActiveMachineExtruderCount(extruder_count)
+ self._application.getMachineManager().setActiveMachineExtruderCount(extruder_count)
@pyqtSlot()
def forceUpdate(self):
# Force rebuilding the build volume by reloading the global container stack.
# This is a bit of a hack, but it seems quick enough.
- Application.getInstance().globalContainerStackChanged.emit()
+ self._application.globalContainerStackChanged.emit()
@pyqtSlot()
def updateHasMaterialsMetadata(self):
@@ -126,9 +130,11 @@ class MachineSettingsAction(MachineAction):
# In other words: only continue for the UM2 (extended), but not for the UM2+
return
- stacks = ExtruderManager.getInstance().getExtruderStacks()
+ machine_manager = self._application.getMachineManager()
+ extruder_positions = list(self._global_container_stack.extruders.keys())
has_materials = self._global_container_stack.getProperty("machine_gcode_flavor", "value") != "UltiGCode"
+ material_node = None
if has_materials:
if "has_materials" in self._global_container_stack.getMetaData():
self._global_container_stack.setMetaDataEntry("has_materials", True)
@@ -136,26 +142,22 @@ class MachineSettingsAction(MachineAction):
self._global_container_stack.addMetaDataEntry("has_materials", True)
# Set the material container for each extruder to a sane default
- for stack in stacks:
- material_container = stack.material
- if material_container == self._empty_container:
- machine_approximate_diameter = str(round(self._global_container_stack.getProperty("material_diameter", "value")))
- search_criteria = { "type": "material", "definition": "fdmprinter", "id": self._global_container_stack.getMetaDataEntry("preferred_material"), "approximate_diameter": machine_approximate_diameter}
- materials = self._container_registry.findInstanceContainers(**search_criteria)
- if materials:
- stack.material = materials[0]
+ material_manager = self._application.getMaterialManager()
+ material_node = material_manager.getDefaultMaterial(self._global_container_stack, None)
+
else:
# The metadata entry is stored in an ini, and ini files are parsed as strings only.
# Because any non-empty string evaluates to a boolean True, we have to remove the entry to make it False.
if "has_materials" in self._global_container_stack.getMetaData():
self._global_container_stack.removeMetaDataEntry("has_materials")
- for stack in stacks:
- stack.material = ContainerRegistry.getInstance().getEmptyInstanceContainer()
+ # set materials
+ for position in extruder_positions:
+ machine_manager.setMaterial(position, material_node)
- Application.getInstance().globalContainerStackChanged.emit()
+ self._application.globalContainerStackChanged.emit()
@pyqtSlot(int)
def updateMaterialForDiameter(self, extruder_position: int):
# Updates the material container to a material that matches the material diameter set for the printer
- Application.getInstance().getExtruderManager().updateMaterialForDiameter(extruder_position)
+ self._application.getExtruderManager().updateMaterialForDiameter(extruder_position)
diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.qml b/plugins/MachineSettingsAction/MachineSettingsAction.qml
index 0aad716300..b12f8f8696 100644
--- a/plugins/MachineSettingsAction/MachineSettingsAction.qml
+++ b/plugins/MachineSettingsAction/MachineSettingsAction.qml
@@ -70,8 +70,8 @@ Cura.MachineAction
anchors.top: pageTitle.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
- property real columnWidth: ((width - 3 * UM.Theme.getSize("default_margin").width) / 2) | 0
- property real labelColumnWidth: columnWidth * 0.5
+ property real columnWidth: Math.round((width - 3 * UM.Theme.getSize("default_margin").width) / 2)
+ property real labelColumnWidth: Math.round(columnWidth / 2)
Tab
{
@@ -165,7 +165,7 @@ Cura.MachineAction
id: gcodeFlavorComboBox
sourceComponent: comboBoxWithOptions
property string settingKey: "machine_gcode_flavor"
- property string label: catalog.i18nc("@label", "Gcode flavor")
+ property string label: catalog.i18nc("@label", "G-code flavor")
property bool forceUpdateOnChange: true
property var afterOnActivate: manager.updateHasMaterialsMetadata
}
@@ -244,6 +244,7 @@ Cura.MachineAction
height: childrenRect.height
width: childrenRect.width
text: machineExtruderCountProvider.properties.description
+ visible: extruderCountModel.count >= 2
Row
{
@@ -307,7 +308,7 @@ Cura.MachineAction
width: settingsTabs.columnWidth
Label
{
- text: catalog.i18nc("@label", "Start Gcode")
+ text: catalog.i18nc("@label", "Start G-code")
font.bold: true
}
Loader
@@ -317,7 +318,7 @@ Cura.MachineAction
property int areaWidth: parent.width
property int areaHeight: parent.height - y
property string settingKey: "machine_start_gcode"
- property string tooltip: catalog.i18nc("@tooltip", "Gcode commands to be executed at the very start.")
+ property string tooltip: catalog.i18nc("@tooltip", "G-code commands to be executed at the very start.")
}
}
@@ -326,7 +327,7 @@ Cura.MachineAction
width: settingsTabs.columnWidth
Label
{
- text: catalog.i18nc("@label", "End Gcode")
+ text: catalog.i18nc("@label", "End G-code")
font.bold: true
}
Loader
@@ -336,7 +337,7 @@ Cura.MachineAction
property int areaWidth: parent.width
property int areaHeight: parent.height - y
property string settingKey: "machine_end_gcode"
- property string tooltip: catalog.i18nc("@tooltip", "Gcode commands to be executed at the very end.")
+ property string tooltip: catalog.i18nc("@tooltip", "G-code commands to be executed at the very end.")
}
}
}
@@ -381,6 +382,11 @@ Cura.MachineAction
property string settingKey: "machine_nozzle_size"
property string label: catalog.i18nc("@label", "Nozzle size")
property string unit: catalog.i18nc("@label", "mm")
+ function afterOnEditingFinished()
+ {
+ // Somehow the machine_nozzle_size dependent settings are not updated otherwise
+ Cura.MachineManager.forceUpdateAllSettings()
+ }
property bool isExtruderSetting: true
}
@@ -441,7 +447,7 @@ Cura.MachineAction
width: settingsTabs.columnWidth
Label
{
- text: catalog.i18nc("@label", "Extruder Start Gcode")
+ text: catalog.i18nc("@label", "Extruder Start G-code")
font.bold: true
}
Loader
@@ -459,7 +465,7 @@ Cura.MachineAction
width: settingsTabs.columnWidth
Label
{
- text: catalog.i18nc("@label", "Extruder End Gcode")
+ text: catalog.i18nc("@label", "Extruder End G-code")
font.bold: true
}
Loader
@@ -888,4 +894,4 @@ Cura.MachineAction
watchedProperties: [ "value" ]
storeIndex: manager.containerIndex
}
-}
\ No newline at end of file
+}
diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py
new file mode 100644
index 0000000000..8a501ceb27
--- /dev/null
+++ b/plugins/ModelChecker/ModelChecker.py
@@ -0,0 +1,116 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+import os
+
+from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, pyqtProperty
+
+from UM.Application import Application
+from UM.Extension import Extension
+from UM.Logger import Logger
+from UM.Message import Message
+from UM.i18n import i18nCatalog
+from UM.PluginRegistry import PluginRegistry
+from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
+
+catalog = i18nCatalog("cura")
+
+
+class ModelChecker(QObject, Extension):
+ ## Signal that gets emitted when anything changed that we need to check.
+ onChanged = pyqtSignal()
+
+ def __init__(self):
+ super().__init__()
+
+ self._button_view = None
+
+ self._caution_message = Message("", #Message text gets set when the message gets shown, to display the models in question.
+ lifetime = 0,
+ title = catalog.i18nc("@info:title", "Model Checker Warning"))
+
+ Application.getInstance().initializationFinished.connect(self._pluginsInitialized)
+ Application.getInstance().getController().getScene().sceneChanged.connect(self._onChanged)
+
+ ## Pass-through to allow UM.Signal to connect with a pyqtSignal.
+ def _onChanged(self, _):
+ self.onChanged.emit()
+
+ ## Called when plug-ins are initialized.
+ #
+ # This makes sure that we listen to changes of the material and that the
+ # button is created that indicates warnings with the current set-up.
+ def _pluginsInitialized(self):
+ Application.getInstance().getMachineManager().rootMaterialChanged.connect(self.onChanged)
+ self._createView()
+
+ def checkObjectsForShrinkage(self):
+ shrinkage_threshold = 0.5 #From what shrinkage percentage a warning will be issued about the model size.
+ warning_size_xy = 150 #The horizontal size of a model that would be too large when dealing with shrinking materials.
+ warning_size_z = 100 #The vertical size of a model that would be too large when dealing with shrinking materials.
+
+ material_shrinkage = self._getMaterialShrinkage()
+
+ warning_nodes = []
+
+ # Check node material shrinkage and bounding box size
+ for node in self.sliceableNodes():
+ node_extruder_position = node.callDecoration("getActiveExtruderPosition")
+ if material_shrinkage[node_extruder_position] > shrinkage_threshold:
+ bbox = node.getBoundingBox()
+ if bbox.width >= warning_size_xy or bbox.depth >= warning_size_xy or bbox.height >= warning_size_z:
+ warning_nodes.append(node)
+
+ self._caution_message.setText(catalog.i18nc(
+ "@info:status",
+ "Some models may not be printed optimal due to object size and chosen material for models: {model_names}.\n"
+ "Tips that may be useful to improve the print quality:\n"
+ "1) Use rounded corners\n"
+ "2) Turn the fan off (only if the are no tiny details on the model)\n"
+ "3) Use a different material").format(model_names = ", ".join([n.getName() for n in warning_nodes])))
+
+ return len(warning_nodes) > 0
+
+ def sliceableNodes(self):
+ # Add all sliceable scene nodes to check
+ scene = Application.getInstance().getController().getScene()
+ for node in DepthFirstIterator(scene.getRoot()):
+ if node.callDecoration("isSliceable"):
+ yield node
+
+ ## Creates the view used by show popup. The view is saved because of the fairly aggressive garbage collection.
+ def _createView(self):
+ Logger.log("d", "Creating model checker view.")
+
+ # Create the plugin dialog component
+ path = os.path.join(PluginRegistry.getInstance().getPluginPath("ModelChecker"), "ModelChecker.qml")
+ self._button_view = Application.getInstance().createQmlComponent(path, {"manager": self})
+
+ # The qml is only the button
+ Application.getInstance().addAdditionalComponent("jobSpecsButton", self._button_view)
+
+ Logger.log("d", "Model checker view created.")
+
+ @pyqtProperty(bool, notify = onChanged)
+ def runChecks(self):
+ danger_shrinkage = self.checkObjectsForShrinkage()
+
+ return any((danger_shrinkage, )) #If any of the checks fail, show the warning button.
+
+ @pyqtSlot()
+ def showWarnings(self):
+ self._caution_message.show()
+
+ def _getMaterialShrinkage(self):
+ global_container_stack = Application.getInstance().getGlobalContainerStack()
+ if global_container_stack is None:
+ return {}
+
+ material_shrinkage = {}
+ # Get all shrinkage values of materials used
+ for extruder_position, extruder in global_container_stack.extruders.items():
+ shrinkage = extruder.material.getProperty("material_shrinkage_percentage", "value")
+ if shrinkage is None:
+ shrinkage = 0
+ material_shrinkage[extruder_position] = shrinkage
+ return material_shrinkage
diff --git a/plugins/ModelChecker/ModelChecker.qml b/plugins/ModelChecker/ModelChecker.qml
new file mode 100644
index 0000000000..3db54d4387
--- /dev/null
+++ b/plugins/ModelChecker/ModelChecker.qml
@@ -0,0 +1,43 @@
+// Copyright (c) 2018 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
+import QtQuick 2.2
+import QtQuick.Controls 1.1
+import QtQuick.Controls.Styles 1.1
+import QtQuick.Layouts 1.1
+import QtQuick.Dialogs 1.1
+import QtQuick.Window 2.2
+
+import UM 1.2 as UM
+import Cura 1.0 as Cura
+
+
+Button
+{
+ id: modelCheckerButton
+
+ UM.I18nCatalog{id: catalog; name:"cura"}
+
+ visible: manager.runChecks
+ tooltip: catalog.i18nc("@info:tooltip", "Some things could be problematic in this print. Click to see tips for adjustment.")
+ onClicked: manager.showWarnings()
+
+ width: UM.Theme.getSize("save_button_specs_icons").width
+ height: UM.Theme.getSize("save_button_specs_icons").height
+
+ style: ButtonStyle
+ {
+ background: Item
+ {
+ UM.RecolorImage
+ {
+ width: UM.Theme.getSize("save_button_specs_icons").width;
+ height: UM.Theme.getSize("save_button_specs_icons").height;
+ sourceSize.width: width;
+ sourceSize.height: width;
+ color: control.hovered ? UM.Theme.getColor("text_scene_hover") : UM.Theme.getColor("text_scene");
+ source: "model_checker.svg"
+ }
+ }
+ }
+}
diff --git a/plugins/ModelChecker/__init__.py b/plugins/ModelChecker/__init__.py
new file mode 100644
index 0000000000..5f4d443729
--- /dev/null
+++ b/plugins/ModelChecker/__init__.py
@@ -0,0 +1,14 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# This example is released under the terms of the AGPLv3 or higher.
+
+from . import ModelChecker
+
+from UM.i18n import i18nCatalog
+i18n_catalog = i18nCatalog("cura")
+
+
+def getMetaData():
+ return {}
+
+def register(app):
+ return { "extension": ModelChecker.ModelChecker() }
diff --git a/plugins/ModelChecker/model_checker.svg b/plugins/ModelChecker/model_checker.svg
new file mode 100644
index 0000000000..ce9594302e
--- /dev/null
+++ b/plugins/ModelChecker/model_checker.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/plugins/ModelChecker/plugin.json b/plugins/ModelChecker/plugin.json
new file mode 100644
index 0000000000..a9190adcaa
--- /dev/null
+++ b/plugins/ModelChecker/plugin.json
@@ -0,0 +1,8 @@
+{
+ "name": "Model Checker",
+ "author": "Ultimaker B.V.",
+ "version": "0.1",
+ "api": 4,
+ "description": "Checks models and print configuration for possible printing issues and give suggestions.",
+ "i18n-catalog": "cura"
+}
diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py
index 1a999ca896..931c205fff 100644
--- a/plugins/MonitorStage/MonitorStage.py
+++ b/plugins/MonitorStage/MonitorStage.py
@@ -22,14 +22,7 @@ class MonitorStage(CuraStage):
def _setActivePrintJob(self, print_job):
if self._active_print_job != print_job:
- if self._active_print_job:
- self._active_print_job.stateChanged.disconnect(self._updateIconSource)
self._active_print_job = print_job
- if self._active_print_job:
- self._active_print_job.stateChanged.connect(self._updateIconSource)
-
- # Ensure that the right icon source is returned.
- self._updateIconSource()
def _setActivePrinter(self, printer):
if self._active_printer != printer:
@@ -43,9 +36,6 @@ class MonitorStage(CuraStage):
else:
self._setActivePrintJob(None)
- # Ensure that the right icon source is returned.
- self._updateIconSource()
-
def _onActivePrintJobChanged(self):
self._setActivePrintJob(self._active_printer.activePrintJob)
@@ -58,19 +48,16 @@ class MonitorStage(CuraStage):
new_output_device = Application.getInstance().getMachineManager().printerOutputDevices[0]
if new_output_device != self._printer_output_device:
if self._printer_output_device:
- self._printer_output_device.acceptsCommandsChanged.disconnect(self._updateIconSource)
- self._printer_output_device.connectionStateChanged.disconnect(self._updateIconSource)
- self._printer_output_device.printersChanged.disconnect(self._onActivePrinterChanged)
+ try:
+ self._printer_output_device.printersChanged.disconnect(self._onActivePrinterChanged)
+ except TypeError:
+ # Ignore stupid "Not connected" errors.
+ pass
self._printer_output_device = new_output_device
- self._printer_output_device.acceptsCommandsChanged.connect(self._updateIconSource)
self._printer_output_device.printersChanged.connect(self._onActivePrinterChanged)
- self._printer_output_device.connectionStateChanged.connect(self._updateIconSource)
self._setActivePrinter(self._printer_output_device.activePrinter)
-
- # Force an update of the icon source
- self._updateIconSource()
except IndexError:
pass
@@ -80,7 +67,6 @@ class MonitorStage(CuraStage):
self._onOutputDevicesChanged()
self._updateMainOverlay()
self._updateSidebar()
- self._updateIconSource()
def _updateMainOverlay(self):
main_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("MonitorStage"), "MonitorMainView.qml")
@@ -90,46 +76,3 @@ class MonitorStage(CuraStage):
# TODO: currently the sidebar component for prepare and monitor stages is the same, this will change with the printer output device refactor!
sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml")
self.addDisplayComponent("sidebar", sidebar_component_path)
-
- def _updateIconSource(self):
- if Application.getInstance().getTheme() is not None:
- icon_name = self._getActiveOutputDeviceStatusIcon()
- self.setIconSource(Application.getInstance().getTheme().getIcon(icon_name))
-
- ## Find the correct status icon depending on the active output device state
- def _getActiveOutputDeviceStatusIcon(self):
- # We assume that you are monitoring the device with the highest priority.
- try:
- output_device = Application.getInstance().getMachineManager().printerOutputDevices[0]
- except IndexError:
- return "tab_status_unknown"
-
- if not output_device.acceptsCommands:
- return "tab_status_unknown"
-
- if output_device.activePrinter is None:
- return "tab_status_connected"
-
- # TODO: refactor to use enum instead of hardcoded strings?
- if output_device.activePrinter.state == "maintenance":
- return "tab_status_busy"
-
- if output_device.activePrinter.activePrintJob is None:
- return "tab_status_connected"
-
- if output_device.activePrinter.activePrintJob.state in ["printing", "pre_print", "pausing", "resuming"]:
- return "tab_status_busy"
-
- if output_device.activePrinter.activePrintJob.state == "wait_cleanup":
- return "tab_status_finished"
-
- if output_device.activePrinter.activePrintJob.state in ["ready", ""]:
- return "tab_status_connected"
-
- if output_device.activePrinter.activePrintJob.state == "paused":
- return "tab_status_paused"
-
- if output_device.activePrinter.activePrintJob.state == "error":
- return "tab_status_stopped"
-
- return "tab_status_unknown"
diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml
index eb492d8de2..a2790dcf08 100644
--- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml
+++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml
@@ -163,7 +163,16 @@ Item {
id: addedSettingsModel;
containerId: Cura.MachineManager.activeDefinitionId
expanded: [ "*" ]
- exclude: {
+ filter:
+ {
+ if (printSequencePropertyProvider.properties.value == "one_at_a_time")
+ {
+ return {"settable_per_meshgroup": true};
+ }
+ return {"settable_per_mesh": true};
+ }
+ exclude:
+ {
var excluded_settings = [ "support_mesh", "anti_overhang_mesh", "cutting_mesh", "infill_mesh" ];
if(meshTypeSelection.model.get(meshTypeSelection.currentIndex).type == "support_mesh")
@@ -237,7 +246,7 @@ Item {
Button
{
- width: Math.floor(UM.Theme.getSize("setting").height / 2)
+ width: Math.round(UM.Theme.getSize("setting").height / 2)
height: UM.Theme.getSize("setting").height
onClicked: addedSettingsModel.setVisible(model.key, false)
@@ -375,7 +384,6 @@ Item {
title: catalog.i18nc("@title:window", "Select Settings to Customize for this model")
width: screenScaleFactor * 360
- property string labelFilter: ""
property var additional_excluded_settings
onVisibilityChanged:
@@ -386,11 +394,33 @@ Item {
// Set skip setting, it will prevent from resetting selected mesh_type
contents.model.visibilityHandler.addSkipResetSetting(meshTypeSelection.model.get(meshTypeSelection.currentIndex).type)
listview.model.forceUpdate()
+
+ updateFilter()
}
}
+ function updateFilter()
+ {
+ var new_filter = {};
+ if (printSequencePropertyProvider.properties.value == "one_at_a_time")
+ {
+ new_filter["settable_per_meshgroup"] = true;
+ }
+ else
+ {
+ new_filter["settable_per_mesh"] = true;
+ }
+
+ if(filterInput.text != "")
+ {
+ new_filter["i18n_label"] = "*" + filterInput.text;
+ }
+
+ listview.model.filter = new_filter;
+ }
+
TextField {
- id: filter
+ id: filterInput
anchors {
top: parent.top
@@ -401,17 +431,7 @@ Item {
placeholderText: catalog.i18nc("@label:textbox", "Filter...");
- onTextChanged:
- {
- if(text != "")
- {
- listview.model.filter = {"settable_per_mesh": true, "i18n_label": "*" + text}
- }
- else
- {
- listview.model.filter = {"settable_per_mesh": true}
- }
- }
+ onTextChanged: settingPickDialog.updateFilter()
}
CheckBox
@@ -437,7 +457,7 @@ Item {
anchors
{
- top: filter.bottom;
+ top: filterInput.bottom;
left: parent.left;
right: parent.right;
bottom: parent.bottom;
@@ -449,10 +469,6 @@ Item {
{
id: definitionsModel;
containerId: Cura.MachineManager.activeDefinitionId
- filter:
- {
- "settable_per_mesh": true
- }
visibilityHandler: UM.SettingPreferenceVisibilityHandler {}
expanded: [ "*" ]
exclude:
@@ -484,6 +500,7 @@ Item {
}
}
}
+ Component.onCompleted: settingPickDialog.updateFilter()
}
}
@@ -507,6 +524,16 @@ Item {
storeIndex: 0
}
+ UM.SettingPropertyProvider
+ {
+ id: printSequencePropertyProvider
+
+ containerStackId: Cura.MachineManager.activeMachineId
+ key: "print_sequence"
+ watchedProperties: [ "value" ]
+ storeIndex: 0
+ }
+
SystemPalette { id: palette; }
Component
diff --git a/plugins/PluginBrowser/PluginBrowser.py b/plugins/PluginBrowser/PluginBrowser.py
index 52cba72478..bb4d5fb395 100644
--- a/plugins/PluginBrowser/PluginBrowser.py
+++ b/plugins/PluginBrowser/PluginBrowser.py
@@ -1,11 +1,10 @@
# Copyright (c) 2017 Ultimaker B.V.
# PluginBrowser is released under the terms of the LGPLv3 or higher.
-from PyQt5.QtCore import QUrl, QObject, Qt, pyqtProperty, pyqtSignal, pyqtSlot
+from PyQt5.QtCore import QUrl, QObject, pyqtProperty, pyqtSignal, pyqtSlot
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
from UM.Application import Application
-from UM.Qt.ListModel import ListModel
from UM.Logger import Logger
from UM.PluginRegistry import PluginRegistry
from UM.Qt.Bindings.PluginsModel import PluginsModel
@@ -20,7 +19,6 @@ import os
import tempfile
import platform
import zipfile
-import shutil
from cura.CuraApplication import CuraApplication
@@ -44,7 +42,7 @@ class PluginBrowser(QObject, Extension):
self._plugins_metadata = []
self._plugins_model = None
- # Can be 'installed' or 'availble'
+ # Can be 'installed' or 'available'
self._view = "available"
self._restart_required = False
@@ -287,8 +285,7 @@ class PluginBrowser(QObject, Extension):
@pyqtProperty(QObject, notify=pluginsMetadataChanged)
def pluginsModel(self):
- print("Updating plugins model...", self._view)
- self._plugins_model = PluginsModel(self._view)
+ self._plugins_model = PluginsModel(None, self._view)
# self._plugins_model.update()
# Check each plugin the registry for matching plugin from server
@@ -367,7 +364,6 @@ class PluginBrowser(QObject, Extension):
# Add metadata to the manager:
self._plugins_metadata = json_data
- print(self._plugins_metadata)
self._plugin_registry.addExternalPlugins(self._plugins_metadata)
self.pluginsMetadataChanged.emit()
except json.decoder.JSONDecodeError:
diff --git a/plugins/PluginBrowser/PluginEntry.qml b/plugins/PluginBrowser/PluginEntry.qml
index 85fc0b91b8..eff9eb8943 100644
--- a/plugins/PluginBrowser/PluginEntry.qml
+++ b/plugins/PluginBrowser/PluginEntry.qml
@@ -164,7 +164,7 @@ Component {
Button {
id: removeButton
text: "Uninstall"
- visible: model.external && model.status == "installed"
+ visible: model.can_uninstall && model.status == "installed"
enabled: !manager.isDownloading
style: ButtonStyle {
background: Rectangle {
diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.py b/plugins/PostProcessingPlugin/PostProcessingPlugin.py
index 7339b379b5..1740710e30 100644
--- a/plugins/PostProcessingPlugin/PostProcessingPlugin.py
+++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.py
@@ -66,7 +66,7 @@ class PostProcessingPlugin(QObject, Extension):
return
# get gcode list for the active build plate
- active_build_plate_id = Application.getInstance().getBuildPlateModel().activeBuildPlate
+ active_build_plate_id = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
gcode_list = gcode_dict[active_build_plate_id]
if not gcode_list:
return
@@ -117,50 +117,39 @@ class PostProcessingPlugin(QObject, Extension):
## Load all scripts from provided path.
# This should probably only be done on init.
# \param path Path to check for scripts.
- def loadAllScripts(self):
+ def loadAllScripts(self, path):
if self._loaded_scripts: #Already loaded.
return
## Load all scripts in the scripts folders
- for root in [PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), Resources.getStoragePath(Resources.Preferences)]:
- try:
- path = os.path.join(root, "scripts")
- if not os.path.isdir(path):
+ scripts = pkgutil.iter_modules(path = [path])
+ for loader, script_name, ispkg in scripts:
+ # Iterate over all scripts.
+ if script_name not in sys.modules:
+ try:
+ spec = importlib.util.spec_from_file_location(__name__ + "." + script_name, os.path.join(path, script_name + ".py"))
+ loaded_script = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(loaded_script)
+ sys.modules[script_name] = loaded_script #TODO: This could be a security risk. Overwrite any module with a user-provided name?
+
+ loaded_class = getattr(loaded_script, script_name)
+ temp_object = loaded_class()
+ Logger.log("d", "Begin loading of script: %s", script_name)
try:
- os.makedirs(path)
- except OSError:
- Logger.log("w", "Unable to create a folder for scripts: " + path)
- continue
-
- scripts = pkgutil.iter_modules(path = [path])
- for loader, script_name, ispkg in scripts:
- # Iterate over all scripts.
- if script_name not in sys.modules:
- spec = importlib.util.spec_from_file_location(__name__ + "." + script_name, os.path.join(path, script_name + ".py"))
- loaded_script = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(loaded_script)
- sys.modules[script_name] = loaded_script #TODO: This could be a security risk. Overwrite any module with a user-provided name?
-
- loaded_class = getattr(loaded_script, script_name)
- temp_object = loaded_class()
- Logger.log("d", "Begin loading of script: %s", script_name)
- try:
- setting_data = temp_object.getSettingData()
- if "name" in setting_data and "key" in setting_data:
- self._script_labels[setting_data["key"]] = setting_data["name"]
- self._loaded_scripts[setting_data["key"]] = loaded_class
- else:
- Logger.log("w", "Script %s.py has no name or key", script_name)
- self._script_labels[script_name] = script_name
- self._loaded_scripts[script_name] = loaded_class
- except AttributeError:
- Logger.log("e", "Script %s.py is not a recognised script type. Ensure it inherits Script", script_name)
- except NotImplementedError:
- Logger.log("e", "Script %s.py has no implemented settings", script_name)
- except Exception as e:
- Logger.logException("e", "Exception occurred while loading post processing plugin: {error_msg}".format(error_msg = str(e)))
-
- self.loadedScriptListChanged.emit()
+ setting_data = temp_object.getSettingData()
+ if "name" in setting_data and "key" in setting_data:
+ self._script_labels[setting_data["key"]] = setting_data["name"]
+ self._loaded_scripts[setting_data["key"]] = loaded_class
+ else:
+ Logger.log("w", "Script %s.py has no name or key", script_name)
+ self._script_labels[script_name] = script_name
+ self._loaded_scripts[script_name] = loaded_class
+ except AttributeError:
+ Logger.log("e", "Script %s.py is not a recognised script type. Ensure it inherits Script", script_name)
+ except NotImplementedError:
+ Logger.log("e", "Script %s.py has no implemented settings", script_name)
+ except Exception as e:
+ Logger.logException("e", "Exception occurred while loading post processing plugin: {error_msg}".format(error_msg = str(e)))
loadedScriptListChanged = pyqtSignal()
@pyqtProperty("QVariantList", notify = loadedScriptListChanged)
@@ -189,7 +178,20 @@ class PostProcessingPlugin(QObject, Extension):
## When the global container stack is changed, swap out the list of active
# scripts.
def _onGlobalContainerStackChanged(self):
- self.loadAllScripts() #Make sure we have all scripts if we didn't have them yet.
+ ## Load all scripts in the scripts folders
+ # The PostProcessingPlugin path is for built-in scripts.
+ # The Resources path is where the user should store custom scripts.
+ # The Preferences path is legacy, where the user may previously have stored scripts.
+ for root in [PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), Resources.getStoragePath(Resources.Resources), Resources.getStoragePath(Resources.Preferences)]:
+ path = os.path.join(root, "scripts")
+ if not os.path.isdir(path):
+ try:
+ os.makedirs(path)
+ except OSError:
+ Logger.log("w", "Unable to create a folder for scripts: " + path)
+ continue
+
+ self.loadAllScripts(path)
new_stack = Application.getInstance().getGlobalContainerStack()
self._script_list.clear()
if not new_stack.getMetaDataEntry("post_processing_scripts"): #Missing or empty.
@@ -246,7 +248,20 @@ class PostProcessingPlugin(QObject, Extension):
def _createView(self):
Logger.log("d", "Creating post processing plugin view.")
- self.loadAllScripts()
+ ## Load all scripts in the scripts folders
+ # The PostProcessingPlugin path is for built-in scripts.
+ # The Resources path is where the user should store custom scripts.
+ # The Preferences path is legacy, where the user may previously have stored scripts.
+ for root in [PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), Resources.getStoragePath(Resources.Resources), Resources.getStoragePath(Resources.Preferences)]:
+ path = os.path.join(root, "scripts")
+ if not os.path.isdir(path):
+ try:
+ os.makedirs(path)
+ except OSError:
+ Logger.log("w", "Unable to create a folder for scripts: " + path)
+ continue
+
+ self.loadAllScripts(path)
# Create the plugin dialog component
path = os.path.join(PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), "PostProcessingPlugin.qml")
diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml
index a7fd2ef9a2..b8d7258ef2 100644
--- a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml
+++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml
@@ -33,8 +33,8 @@ UM.Dialog
{
UM.I18nCatalog{id: catalog; name:"cura"}
id: base
- property int columnWidth: Math.floor((base.width / 2) - UM.Theme.getSize("default_margin").width)
- property int textMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2)
+ property int columnWidth: Math.round((base.width / 2) - UM.Theme.getSize("default_margin").width)
+ property int textMargin: Math.round(UM.Theme.getSize("default_margin").width / 2)
property string activeScriptName
SystemPalette{ id: palette }
SystemPalette{ id: disabledPalette; colorGroup: SystemPalette.Disabled }
@@ -137,8 +137,8 @@ UM.Dialog
{
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
- width: Math.floor(control.width / 2.7)
- height: Math.floor(control.height / 2.7)
+ width: Math.round(control.width / 2.7)
+ height: Math.round(control.height / 2.7)
sourceSize.width: width
sourceSize.height: width
color: palette.text
@@ -172,8 +172,8 @@ UM.Dialog
{
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
- width: Math.floor(control.width / 2.5)
- height: Math.floor(control.height / 2.5)
+ width: Math.round(control.width / 2.5)
+ height: Math.round(control.height / 2.5)
sourceSize.width: width
sourceSize.height: width
color: control.enabled ? palette.text : disabledPalette.text
@@ -207,8 +207,8 @@ UM.Dialog
{
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
- width: Math.floor(control.width / 2.5)
- height: Math.floor(control.height / 2.5)
+ width: Math.round(control.width / 2.5)
+ height: Math.round(control.height / 2.5)
sourceSize.width: width
sourceSize.height: width
color: control.enabled ? palette.text : disabledPalette.text
@@ -486,15 +486,15 @@ UM.Dialog
control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button")
Behavior on color { ColorAnimation { duration: 50; } }
anchors.left: parent.left
- anchors.leftMargin: Math.floor(UM.Theme.getSize("save_button_text_margin").width / 2);
+ anchors.leftMargin: Math.round(UM.Theme.getSize("save_button_text_margin").width / 2);
width: parent.height
height: parent.height
UM.RecolorImage {
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
- width: Math.floor(parent.width / 2)
- height: Math.floor(parent.height / 2)
+ width: Math.round(parent.width / 2)
+ height: Math.round(parent.height / 2)
sourceSize.width: width
sourceSize.height: height
color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") :
diff --git a/plugins/PostProcessingPlugin/Script.py b/plugins/PostProcessingPlugin/Script.py
index 7d603ba11f..7f419cd422 100644
--- a/plugins/PostProcessingPlugin/Script.py
+++ b/plugins/PostProcessingPlugin/Script.py
@@ -105,6 +105,58 @@ class Script:
except:
return default
+ ## Convenience function to produce a line of g-code.
+ #
+ # You can put in an original g-code line and it'll re-use all the values
+ # in that line.
+ # All other keyword parameters are put in the result in g-code's format.
+ # For instance, if you put ``G=1`` in the parameters, it will output
+ # ``G1``. If you put ``G=1, X=100`` in the parameters, it will output
+ # ``G1 X100``. The parameters G and M will always be put first. The
+ # parameters T and S will be put second (or first if there is no G or M).
+ # The rest of the parameters will be put in arbitrary order.
+ # \param line The original g-code line that must be modified. If not
+ # provided, an entirely new g-code line will be produced.
+ # \return A line of g-code with the desired parameters filled in.
+ def putValue(self, line = "", **kwargs):
+ #Strip the comment.
+ comment = ""
+ if ";" in line:
+ comment = line[line.find(";"):]
+ line = line[:line.find(";")] #Strip the comment.
+
+ #Parse the original g-code line.
+ for part in line.split(" "):
+ if part == "":
+ continue
+ parameter = part[0]
+ if parameter in kwargs:
+ continue #Skip this one. The user-provided parameter overwrites the one in the line.
+ value = part[1:]
+ kwargs[parameter] = value
+
+ #Write the new g-code line.
+ result = ""
+ priority_parameters = ["G", "M", "T", "S", "F", "X", "Y", "Z", "E"] #First some parameters that get priority. In order of priority!
+ for priority_key in priority_parameters:
+ if priority_key in kwargs:
+ if result != "":
+ result += " "
+ result += priority_key + str(kwargs[priority_key])
+ del kwargs[priority_key]
+ for key, value in kwargs.items():
+ if result != "":
+ result += " "
+ result += key + str(value)
+
+ #Put the comment back in.
+ if comment != "":
+ if result != "":
+ result += " "
+ result += ";" + comment
+
+ return result
+
## This is called when the script is executed.
# It gets a list of g-code strings and needs to return a (modified) list.
def execute(self, data):
diff --git a/plugins/PostProcessingPlugin/scripts/TweakAtZ.py b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py
similarity index 78%
rename from plugins/PostProcessingPlugin/scripts/TweakAtZ.py
rename to plugins/PostProcessingPlugin/scripts/ChangeAtZ.py
index 7b714f6ee0..54d6fdb155 100644
--- a/plugins/PostProcessingPlugin/scripts/TweakAtZ.py
+++ b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py
@@ -1,10 +1,10 @@
-# TweakAtZ script - Change printing parameters at a given height
+# ChangeAtZ script - Change printing parameters at a given height
# This script is the successor of the TweakAtZ plugin for legacy Cura.
# It contains code from the TweakAtZ plugin V1.0-V4.x and from the ExampleScript by Jaime van Kessel, Ultimaker B.V.
# It runs with the PostProcessingPlugin which is released under the terms of the AGPLv3 or higher.
# This script is licensed under the Creative Commons - Attribution - Share Alike (CC BY-SA) terms
-#Authors of the TweakAtZ plugin / script:
+#Authors of the ChangeAtZ plugin / script:
# Written by Steven Morlock, smorloc@gmail.com
# Modified by Ricardo Gomez, ricardoga@otulook.com, to add Bed Temperature and make it work with Cura_13.06.04+
# Modified by Stefan Heule, Dim3nsioneer@gmx.ch since V3.0 (see changelog below)
@@ -46,15 +46,15 @@ from ..Script import Script
#from UM.Logger import Logger
import re
-class TweakAtZ(Script):
+class ChangeAtZ(Script):
version = "5.1.1"
def __init__(self):
super().__init__()
def getSettingDataString(self):
return """{
- "name":"TweakAtZ """ + self.version + """ (Experimental)",
- "key":"TweakAtZ",
+ "name":"ChangeAtZ """ + self.version + """ (Experimental)",
+ "key":"ChangeAtZ",
"metadata": {},
"version": 2,
"settings":
@@ -69,8 +69,8 @@ class TweakAtZ(Script):
},
"b_targetZ":
{
- "label": "Tweak Height",
- "description": "Z height to tweak at",
+ "label": "Change Height",
+ "description": "Z height to change at",
"unit": "mm",
"type": "float",
"default_value": 5.0,
@@ -81,8 +81,8 @@ class TweakAtZ(Script):
},
"b_targetL":
{
- "label": "Tweak Layer",
- "description": "Layer no. to tweak at",
+ "label": "Change Layer",
+ "description": "Layer no. to change at",
"unit": "",
"type": "int",
"default_value": 1,
@@ -93,7 +93,7 @@ class TweakAtZ(Script):
"c_behavior":
{
"label": "Behavior",
- "description": "Select behavior: Tweak value and keep it for the rest, Tweak value for single layer only",
+ "description": "Select behavior: Change value and keep it for the rest, Change value for single layer only",
"type": "enum",
"options": {"keep_value":"Keep value","single_layer":"Single Layer"},
"default_value": "keep_value"
@@ -101,7 +101,7 @@ class TweakAtZ(Script):
"d_twLayers":
{
"label": "No. Layers",
- "description": "No. of layers used to tweak",
+ "description": "No. of layers used to change",
"unit": "",
"type": "int",
"default_value": 1,
@@ -109,10 +109,10 @@ class TweakAtZ(Script):
"maximum_value_warning": "50",
"enabled": "c_behavior == 'keep_value'"
},
- "e1_Tweak_speed":
+ "e1_Change_speed":
{
- "label": "Tweak Speed",
- "description": "Select if total speed (print and travel) has to be tweaked",
+ "label": "Change Speed",
+ "description": "Select if total speed (print and travel) has to be cahnged",
"type": "bool",
"default_value": false
},
@@ -126,12 +126,12 @@ class TweakAtZ(Script):
"minimum_value": "1",
"minimum_value_warning": "10",
"maximum_value_warning": "200",
- "enabled": "e1_Tweak_speed"
+ "enabled": "e1_Change_speed"
},
- "f1_Tweak_printspeed":
+ "f1_Change_printspeed":
{
- "label": "Tweak Print Speed",
- "description": "Select if print speed has to be tweaked",
+ "label": "Change Print Speed",
+ "description": "Select if print speed has to be changed",
"type": "bool",
"default_value": false
},
@@ -145,12 +145,12 @@ class TweakAtZ(Script):
"minimum_value": "1",
"minimum_value_warning": "10",
"maximum_value_warning": "200",
- "enabled": "f1_Tweak_printspeed"
+ "enabled": "f1_Change_printspeed"
},
- "g1_Tweak_flowrate":
+ "g1_Change_flowrate":
{
- "label": "Tweak Flow Rate",
- "description": "Select if flow rate has to be tweaked",
+ "label": "Change Flow Rate",
+ "description": "Select if flow rate has to be changed",
"type": "bool",
"default_value": false
},
@@ -164,12 +164,12 @@ class TweakAtZ(Script):
"minimum_value": "1",
"minimum_value_warning": "10",
"maximum_value_warning": "200",
- "enabled": "g1_Tweak_flowrate"
+ "enabled": "g1_Change_flowrate"
},
- "g3_Tweak_flowrateOne":
+ "g3_Change_flowrateOne":
{
- "label": "Tweak Flow Rate 1",
- "description": "Select if first extruder flow rate has to be tweaked",
+ "label": "Change Flow Rate 1",
+ "description": "Select if first extruder flow rate has to be changed",
"type": "bool",
"default_value": false
},
@@ -183,12 +183,12 @@ class TweakAtZ(Script):
"minimum_value": "1",
"minimum_value_warning": "10",
"maximum_value_warning": "200",
- "enabled": "g3_Tweak_flowrateOne"
+ "enabled": "g3_Change_flowrateOne"
},
- "g5_Tweak_flowrateTwo":
+ "g5_Change_flowrateTwo":
{
- "label": "Tweak Flow Rate 2",
- "description": "Select if second extruder flow rate has to be tweaked",
+ "label": "Change Flow Rate 2",
+ "description": "Select if second extruder flow rate has to be changed",
"type": "bool",
"default_value": false
},
@@ -202,12 +202,12 @@ class TweakAtZ(Script):
"minimum_value": "1",
"minimum_value_warning": "10",
"maximum_value_warning": "200",
- "enabled": "g5_Tweak_flowrateTwo"
+ "enabled": "g5_Change_flowrateTwo"
},
- "h1_Tweak_bedTemp":
+ "h1_Change_bedTemp":
{
- "label": "Tweak Bed Temp",
- "description": "Select if Bed Temperature has to be tweaked",
+ "label": "Change Bed Temp",
+ "description": "Select if Bed Temperature has to be changed",
"type": "bool",
"default_value": false
},
@@ -221,12 +221,12 @@ class TweakAtZ(Script):
"minimum_value": "0",
"minimum_value_warning": "30",
"maximum_value_warning": "120",
- "enabled": "h1_Tweak_bedTemp"
+ "enabled": "h1_Change_bedTemp"
},
- "i1_Tweak_extruderOne":
+ "i1_Change_extruderOne":
{
- "label": "Tweak Extruder 1 Temp",
- "description": "Select if First Extruder Temperature has to be tweaked",
+ "label": "Change Extruder 1 Temp",
+ "description": "Select if First Extruder Temperature has to be changed",
"type": "bool",
"default_value": false
},
@@ -240,12 +240,12 @@ class TweakAtZ(Script):
"minimum_value": "0",
"minimum_value_warning": "160",
"maximum_value_warning": "250",
- "enabled": "i1_Tweak_extruderOne"
+ "enabled": "i1_Change_extruderOne"
},
- "i3_Tweak_extruderTwo":
+ "i3_Change_extruderTwo":
{
- "label": "Tweak Extruder 2 Temp",
- "description": "Select if Second Extruder Temperature has to be tweaked",
+ "label": "Change Extruder 2 Temp",
+ "description": "Select if Second Extruder Temperature has to be changed",
"type": "bool",
"default_value": false
},
@@ -259,12 +259,12 @@ class TweakAtZ(Script):
"minimum_value": "0",
"minimum_value_warning": "160",
"maximum_value_warning": "250",
- "enabled": "i3_Tweak_extruderTwo"
+ "enabled": "i3_Change_extruderTwo"
},
- "j1_Tweak_fanSpeed":
+ "j1_Change_fanSpeed":
{
- "label": "Tweak Fan Speed",
- "description": "Select if Fan Speed has to be tweaked",
+ "label": "Change Fan Speed",
+ "description": "Select if Fan Speed has to be changed",
"type": "bool",
"default_value": false
},
@@ -278,17 +278,17 @@ class TweakAtZ(Script):
"minimum_value": "0",
"minimum_value_warning": "15",
"maximum_value_warning": "255",
- "enabled": "j1_Tweak_fanSpeed"
+ "enabled": "j1_Change_fanSpeed"
}
}
}"""
def getValue(self, line, key, default = None): #replace default getvalue due to comment-reading feature
if not key in line or (";" in line and line.find(key) > line.find(";") and
- not ";TweakAtZ" in key and not ";LAYER:" in key):
+ not ";ChangeAtZ" in key and not ";LAYER:" in key):
return default
subPart = line[line.find(key) + len(key):] #allows for string lengths larger than 1
- if ";TweakAtZ" in key:
+ if ";ChangeAtZ" in key:
m = re.search("^[0-4]", subPart)
elif ";LAYER:" in key:
m = re.search("^[+-]?[0-9]*", subPart)
@@ -303,17 +303,17 @@ class TweakAtZ(Script):
return default
def execute(self, data):
- #Check which tweaks should apply
- TweakProp = {"speed": self.getSettingValueByKey("e1_Tweak_speed"),
- "flowrate": self.getSettingValueByKey("g1_Tweak_flowrate"),
- "flowrateOne": self.getSettingValueByKey("g3_Tweak_flowrateOne"),
- "flowrateTwo": self.getSettingValueByKey("g5_Tweak_flowrateTwo"),
- "bedTemp": self.getSettingValueByKey("h1_Tweak_bedTemp"),
- "extruderOne": self.getSettingValueByKey("i1_Tweak_extruderOne"),
- "extruderTwo": self.getSettingValueByKey("i3_Tweak_extruderTwo"),
- "fanSpeed": self.getSettingValueByKey("j1_Tweak_fanSpeed")}
- TweakPrintSpeed = self.getSettingValueByKey("f1_Tweak_printspeed")
- TweakStrings = {"speed": "M220 S%f\n",
+ #Check which changes should apply
+ ChangeProp = {"speed": self.getSettingValueByKey("e1_Change_speed"),
+ "flowrate": self.getSettingValueByKey("g1_Change_flowrate"),
+ "flowrateOne": self.getSettingValueByKey("g3_Change_flowrateOne"),
+ "flowrateTwo": self.getSettingValueByKey("g5_Change_flowrateTwo"),
+ "bedTemp": self.getSettingValueByKey("h1_Change_bedTemp"),
+ "extruderOne": self.getSettingValueByKey("i1_Change_extruderOne"),
+ "extruderTwo": self.getSettingValueByKey("i3_Change_extruderTwo"),
+ "fanSpeed": self.getSettingValueByKey("j1_Change_fanSpeed")}
+ ChangePrintSpeed = self.getSettingValueByKey("f1_Change_printspeed")
+ ChangeStrings = {"speed": "M220 S%f\n",
"flowrate": "M221 S%f\n",
"flowrateOne": "M221 T0 S%f\n",
"flowrateTwo": "M221 T1 S%f\n",
@@ -369,14 +369,14 @@ class TweakAtZ(Script):
for line in lines:
if ";Generated with Cura_SteamEngine" in line:
TWinstances += 1
- modified_gcode += ";TweakAtZ instances: %d\n" % TWinstances
- if not ("M84" in line or "M25" in line or ("G1" in line and TweakPrintSpeed and (state==3 or state==4)) or
- ";TweakAtZ instances:" in line):
+ modified_gcode += ";ChangeAtZ instances: %d\n" % TWinstances
+ if not ("M84" in line or "M25" in line or ("G1" in line and ChangePrintSpeed and (state==3 or state==4)) or
+ ";ChangeAtZ instances:" in line):
modified_gcode += line + "\n"
IsUM2 = ("FLAVOR:UltiGCode" in line) or IsUM2 #Flavor is UltiGCode!
- if ";TweakAtZ-state" in line: #checks for state change comment
- state = self.getValue(line, ";TweakAtZ-state", state)
- if ";TweakAtZ instances:" in line:
+ if ";ChangeAtZ-state" in line: #checks for state change comment
+ state = self.getValue(line, ";ChangeAtZ-state", state)
+ if ";ChangeAtZ instances:" in line:
try:
tempTWi = int(line[20:])
except:
@@ -390,7 +390,7 @@ class TweakAtZ(Script):
state = old["state"]
layer = self.getValue(line, ";LAYER:", layer)
if targetL_i > -100000: #target selected by layer no.
- if (state == 2 or targetL_i == 0) and layer == targetL_i: #determine targetZ from layer no.; checks for tweak on layer 0
+ if (state == 2 or targetL_i == 0) and layer == targetL_i: #determine targetZ from layer no.; checks for change on layer 0
state = 2
targetZ = z + 0.001
if (self.getValue(line, "T", None) is not None) and (self.getValue(line, "M", None) is None): #looking for single T-cmd
@@ -415,7 +415,7 @@ class TweakAtZ(Script):
elif tmp_extruder == 1: #second extruder
old["flowrateOne"] = self.getValue(line, "S", old["flowrateOne"])
if ("M84" in line or "M25" in line):
- if state>0 and TweakProp["speed"]: #"finish" commands for UM Original and UM2
+ if state>0 and ChangeProp["speed"]: #"finish" commands for UM Original and UM2
modified_gcode += "M220 S100 ; speed reset to 100% at the end of print\n"
modified_gcode += "M117 \n"
modified_gcode += line + "\n"
@@ -425,14 +425,14 @@ class TweakAtZ(Script):
y = self.getValue(line, "Y", None)
e = self.getValue(line, "E", None)
f = self.getValue(line, "F", None)
- if 'G1' in line and TweakPrintSpeed and (state==3 or state==4):
+ if 'G1' in line and ChangePrintSpeed and (state==3 or state==4):
# check for pure print movement in target range:
if x != None and y != None and f != None and e != None and newZ==z:
modified_gcode += "G1 F%d X%1.3f Y%1.3f E%1.5f\n" % (int(f / 100.0 * float(target_values["printspeed"])), self.getValue(line, "X"),
self.getValue(line, "Y"), self.getValue(line, "E"))
else: #G1 command but not a print movement
modified_gcode += line + "\n"
- # no tweaking on retraction hops which have no x and y coordinate:
+ # no changing on retraction hops which have no x and y coordinate:
if (newZ != z) and (x is not None) and (y is not None):
z = newZ
if z < targetZ and state == 1:
@@ -440,56 +440,56 @@ class TweakAtZ(Script):
if z >= targetZ and state == 2:
state = 3
done_layers = 0
- for key in TweakProp:
- if TweakProp[key] and old[key]==-1: #old value is not known
+ for key in ChangeProp:
+ if ChangeProp[key] and old[key]==-1: #old value is not known
oldValueUnknown = True
- if oldValueUnknown: #the tweaking has to happen within one layer
+ if oldValueUnknown: #the changing has to happen within one layer
twLayers = 1
if IsUM2: #Parameters have to be stored in the printer (UltiGCode=UM2)
- modified_gcode += "M605 S%d;stores parameters before tweaking\n" % (TWinstances-1)
- if behavior == 1: #single layer tweak only and then reset
+ modified_gcode += "M605 S%d;stores parameters before changing\n" % (TWinstances-1)
+ if behavior == 1: #single layer change only and then reset
twLayers = 1
- if TweakPrintSpeed and behavior == 0:
+ if ChangePrintSpeed and behavior == 0:
twLayers = done_layers + 1
if state==3:
if twLayers-done_layers>0: #still layers to go?
if targetL_i > -100000:
- modified_gcode += ";TweakAtZ V%s: executed at Layer %d\n" % (self.version,layer)
- modified_gcode += "M117 Printing... tw@L%4d\n" % layer
+ modified_gcode += ";ChangeAtZ V%s: executed at Layer %d\n" % (self.version,layer)
+ modified_gcode += "M117 Printing... ch@L%4d\n" % layer
else:
- modified_gcode += (";TweakAtZ V%s: executed at %1.2f mm\n" % (self.version,z))
- modified_gcode += "M117 Printing... tw@%5.1f\n" % z
- for key in TweakProp:
- if TweakProp[key]:
- modified_gcode += TweakStrings[key] % float(old[key]+(float(target_values[key])-float(old[key]))/float(twLayers)*float(done_layers+1))
+ modified_gcode += (";ChangeAtZ V%s: executed at %1.2f mm\n" % (self.version,z))
+ modified_gcode += "M117 Printing... ch@%5.1f\n" % z
+ for key in ChangeProp:
+ if ChangeProp[key]:
+ modified_gcode += ChangeStrings[key] % float(old[key]+(float(target_values[key])-float(old[key]))/float(twLayers)*float(done_layers+1))
done_layers += 1
else:
state = 4
if behavior == 1: #reset values after one layer
if targetL_i > -100000:
- modified_gcode += ";TweakAtZ V%s: reset on Layer %d\n" % (self.version,layer)
+ modified_gcode += ";ChangeAtZ V%s: reset on Layer %d\n" % (self.version,layer)
else:
- modified_gcode += ";TweakAtZ V%s: reset at %1.2f mm\n" % (self.version,z)
+ modified_gcode += ";ChangeAtZ V%s: reset at %1.2f mm\n" % (self.version,z)
if IsUM2 and oldValueUnknown: #executes on UM2 with Ultigcode and machine setting
modified_gcode += "M606 S%d;recalls saved settings\n" % (TWinstances-1)
else: #executes on RepRap, UM2 with Ultigcode and Cura setting
- for key in TweakProp:
- if TweakProp[key]:
- modified_gcode += TweakStrings[key] % float(old[key])
+ for key in ChangeProp:
+ if ChangeProp[key]:
+ modified_gcode += ChangeStrings[key] % float(old[key])
# re-activates the plugin if executed by pre-print G-command, resets settings:
- if (z < targetZ or layer == 0) and state >= 3: #resets if below tweak level or at level 0
+ if (z < targetZ or layer == 0) and state >= 3: #resets if below change level or at level 0
state = 2
done_layers = 0
if targetL_i > -100000:
- modified_gcode += ";TweakAtZ V%s: reset below Layer %d\n" % (self.version,targetL_i)
+ modified_gcode += ";ChangeAtZ V%s: reset below Layer %d\n" % (self.version,targetL_i)
else:
- modified_gcode += ";TweakAtZ V%s: reset below %1.2f mm\n" % (self.version,targetZ)
+ modified_gcode += ";ChangeAtZ V%s: reset below %1.2f mm\n" % (self.version,targetZ)
if IsUM2 and oldValueUnknown: #executes on UM2 with Ultigcode and machine setting
modified_gcode += "M606 S%d;recalls saved settings\n" % (TWinstances-1)
else: #executes on RepRap, UM2 with Ultigcode and Cura setting
- for key in TweakProp:
- if TweakProp[key]:
- modified_gcode += TweakStrings[key] % float(old[key])
+ for key in ChangeProp:
+ if ChangeProp[key]:
+ modified_gcode += ChangeStrings[key] % float(old[key])
data[index] = modified_gcode
index += 1
return data
diff --git a/plugins/PostProcessingPlugin/scripts/ColorChange.py b/plugins/PostProcessingPlugin/scripts/FilamentChange.py
similarity index 75%
rename from plugins/PostProcessingPlugin/scripts/ColorChange.py
rename to plugins/PostProcessingPlugin/scripts/FilamentChange.py
index 8db45f4033..07e887b082 100644
--- a/plugins/PostProcessingPlugin/scripts/ColorChange.py
+++ b/plugins/PostProcessingPlugin/scripts/FilamentChange.py
@@ -2,17 +2,15 @@
# under the terms of the AGPLv3 or higher
from ..Script import Script
-#from UM.Logger import Logger
-# from cura.Settings.ExtruderManager import ExtruderManager
-class ColorChange(Script):
+class FilamentChange(Script):
def __init__(self):
super().__init__()
def getSettingDataString(self):
return """{
- "name":"Color Change",
- "key": "ColorChange",
+ "name":"Filament Change",
+ "key": "FilamentChange",
"metadata": {},
"version": 2,
"settings":
@@ -29,18 +27,18 @@ class ColorChange(Script):
"initial_retract":
{
"label": "Initial Retraction",
- "description": "Initial filament retraction distance",
+ "description": "Initial filament retraction distance. The filament will be retracted with this amount before moving the nozzle away from the ongoing print.",
"unit": "mm",
"type": "float",
- "default_value": 300.0
+ "default_value": 30.0
},
"later_retract":
{
"label": "Later Retraction Distance",
- "description": "Later filament retraction distance for removal",
+ "description": "Later filament retraction distance for removal. The filament will be retracted all the way out of the printer so that you can change the filament.",
"unit": "mm",
"type": "float",
- "default_value": 30.0
+ "default_value": 300.0
}
}
}"""
@@ -60,17 +58,17 @@ class ColorChange(Script):
if later_retract is not None and later_retract > 0.:
color_change = color_change + (" L%.2f" % later_retract)
- color_change = color_change + " ; Generated by ColorChange plugin"
+ color_change = color_change + " ; Generated by FilamentChange plugin"
- layer_targets = layer_nums.split(',')
+ layer_targets = layer_nums.split(",")
if len(layer_targets) > 0:
for layer_num in layer_targets:
- layer_num = int( layer_num.strip() )
+ layer_num = int(layer_num.strip())
if layer_num < len(data):
- layer = data[ layer_num - 1 ]
+ layer = data[layer_num - 1]
lines = layer.split("\n")
- lines.insert(2, color_change )
- final_line = "\n".join( lines )
- data[ layer_num - 1 ] = final_line
+ lines.insert(2, color_change)
+ final_line = "\n".join(lines)
+ data[layer_num - 1] = final_line
return data
diff --git a/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py b/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py
index 925a5a7ac5..805ab0a2c3 100644
--- a/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py
+++ b/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py
@@ -7,19 +7,40 @@ class PauseAtHeight(Script):
def getSettingDataString(self):
return """{
- "name":"Pause at height",
+ "name": "Pause at height",
"key": "PauseAtHeight",
"metadata": {},
"version": 2,
"settings":
{
+ "pause_at":
+ {
+ "label": "Pause at",
+ "description": "Whether to pause at a certain height or at a certain layer.",
+ "type": "enum",
+ "options": {"height": "Height", "layer_no": "Layer No."},
+ "default_value": "height"
+ },
"pause_height":
{
"label": "Pause Height",
"description": "At what height should the pause occur",
"unit": "mm",
"type": "float",
- "default_value": 5.0
+ "default_value": 5.0,
+ "minimum_value": "0",
+ "minimum_value_warning": "0.27",
+ "enabled": "pause_at == 'height'"
+ },
+ "pause_layer":
+ {
+ "label": "Pause Layer",
+ "description": "At what layer should the pause occur",
+ "type": "int",
+ "value": "math.floor((pause_height - 0.27) / 0.1) + 1",
+ "minimum_value": "0",
+ "minimum_value_warning": "1",
+ "enabled": "pause_at == 'layer_no'"
},
"head_park_x":
{
@@ -102,8 +123,9 @@ class PauseAtHeight(Script):
x = 0.
y = 0.
- current_z = 0.
+ pause_at = self.getSettingValueByKey("pause_at")
pause_height = self.getSettingValueByKey("pause_height")
+ pause_layer = self.getSettingValueByKey("pause_layer")
retraction_amount = self.getSettingValueByKey("retraction_amount")
retraction_speed = self.getSettingValueByKey("retraction_speed")
extrude_amount = self.getSettingValueByKey("extrude_amount")
@@ -121,101 +143,133 @@ class PauseAtHeight(Script):
# use offset to calculate the current height: = -
layer_0_z = 0.
+ current_z = 0
got_first_g_cmd_on_layer_0 = False
- for layer in data:
+ for index, layer in enumerate(data):
lines = layer.split("\n")
for line in lines:
if ";LAYER:0" in line:
layers_started = True
- continue
-
if not layers_started:
continue
- if self.getValue(line, 'G') == 1 or self.getValue(line, 'G') == 0:
- current_z = self.getValue(line, 'Z')
+ if self.getValue(line, "Z") is not None:
+ current_z = self.getValue(line, "Z")
+
+ if pause_at == "height":
+ if self.getValue(line, "G") != 1 and self.getValue(line, "G") != 0:
+ continue
+
if not got_first_g_cmd_on_layer_0:
layer_0_z = current_z
got_first_g_cmd_on_layer_0 = True
- x = self.getValue(line, 'X', x)
- y = self.getValue(line, 'Y', y)
- if current_z is not None:
- current_height = current_z - layer_0_z
- if current_height >= pause_height:
- index = data.index(layer)
- prevLayer = data[index - 1]
- prevLines = prevLayer.split("\n")
- current_e = 0.
- for prevLine in reversed(prevLines):
- current_e = self.getValue(prevLine, 'E', -1)
- if current_e >= 0:
- break
+ x = self.getValue(line, "X", x)
+ y = self.getValue(line, "Y", y)
- # include a number of previous layers
- for i in range(1, redo_layers + 1):
- prevLayer = data[index - i]
- layer = prevLayer + layer
+ current_height = current_z - layer_0_z
+ if current_height < pause_height:
+ break #Try the next layer.
+ else: #Pause at layer.
+ if not line.startswith(";LAYER:"):
+ continue
+ current_layer = line[len(";LAYER:"):]
+ try:
+ current_layer = int(current_layer)
+ except ValueError: #Couldn't cast to int. Something is wrong with this g-code data.
+ continue
+ if current_layer < pause_layer:
+ break #Try the next layer.
- prepend_gcode = ";TYPE:CUSTOM\n"
- prepend_gcode += ";added code by post processing\n"
- prepend_gcode += ";script: PauseAtHeight.py\n"
- prepend_gcode += ";current z: %f \n" % current_z
- prepend_gcode += ";current height: %f \n" % current_height
+ prevLayer = data[index - 1]
+ prevLines = prevLayer.split("\n")
+ current_e = 0.
- # Retraction
- prepend_gcode += "M83\n"
- if retraction_amount != 0:
- prepend_gcode += "G1 E-%f F%f\n" % (retraction_amount, retraction_speed * 60)
-
- # Move the head away
- prepend_gcode += "G1 Z%f F300\n" % (current_z + 1)
- prepend_gcode += "G1 X%f Y%f F9000\n" % (park_x, park_y)
- if current_z < 15:
- prepend_gcode += "G1 Z15 F300\n"
-
- # Disable the E steppers
- prepend_gcode += "M84 E0\n"
-
- # Set extruder standby temperature
- prepend_gcode += "M104 S%i; standby temperature\n" % (standby_temperature)
-
- # Wait till the user continues printing
- prepend_gcode += "M0 ;Do the actual pause\n"
-
- # Set extruder resume temperature
- prepend_gcode += "M109 S%i; resume temperature\n" % (resume_temperature)
-
- # Push the filament back,
- if retraction_amount != 0:
- prepend_gcode += "G1 E%f F%f\n" % (retraction_amount, retraction_speed * 60)
-
- # Optionally extrude material
- if extrude_amount != 0:
- prepend_gcode += "G1 E%f F%f\n" % (extrude_amount, extrude_speed * 60)
-
- # and retract again, the properly primes the nozzle
- # when changing filament.
- if retraction_amount != 0:
- prepend_gcode += "G1 E-%f F%f\n" % (retraction_amount, retraction_speed * 60)
-
- # Move the head back
- prepend_gcode += "G1 Z%f F300\n" % (current_z + 1)
- prepend_gcode += "G1 X%f Y%f F9000\n" % (x, y)
- if retraction_amount != 0:
- prepend_gcode += "G1 E%f F%f\n" % (retraction_amount, retraction_speed * 60)
- prepend_gcode += "G1 F9000\n"
- prepend_gcode += "M82\n"
-
- # reset extrude value to pre pause value
- prepend_gcode += "G92 E%f\n" % (current_e)
-
- layer = prepend_gcode + layer
-
-
- # Override the data of this layer with the
- # modified data
- data[index] = layer
- return data
+ # Access last layer, browse it backwards to find
+ # last extruder absolute position
+ for prevLine in reversed(prevLines):
+ current_e = self.getValue(prevLine, "E", -1)
+ if current_e >= 0:
break
+
+ # include a number of previous layers
+ for i in range(1, redo_layers + 1):
+ prevLayer = data[index - i]
+ layer = prevLayer + layer
+
+ # Get extruder's absolute position at the
+ # begining of the first layer redone
+ # see https://github.com/nallath/PostProcessingPlugin/issues/55
+ if i == redo_layers:
+ prevLines = prevLayer.split("\n")
+ for line in prevLines:
+ new_e = self.getValue(line, 'E', current_e)
+
+ if new_e != current_e:
+ current_e = new_e
+ break
+
+ prepend_gcode = ";TYPE:CUSTOM\n"
+ prepend_gcode += ";added code by post processing\n"
+ prepend_gcode += ";script: PauseAtHeight.py\n"
+ if pause_at == "height":
+ prepend_gcode += ";current z: {z}\n".format(z = current_z)
+ prepend_gcode += ";current height: {height}\n".format(height = current_height)
+ else:
+ prepend_gcode += ";current layer: {layer}\n".format(layer = current_layer)
+
+ # Retraction
+ prepend_gcode += self.putValue(M = 83) + "\n"
+ if retraction_amount != 0:
+ prepend_gcode += self.putValue(G = 1, E = -retraction_amount, F = retraction_speed * 60) + "\n"
+
+ # Move the head away
+ prepend_gcode += self.putValue(G = 1, Z = current_z + 1, F = 300) + "\n"
+ prepend_gcode += self.putValue(G = 1, X = park_x, Y = park_y, F = 9000) + "\n"
+ 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"
+
+ # Wait till the user continues printing
+ 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"
+
+ # Push the filament back,
+ if retraction_amount != 0:
+ prepend_gcode += self.putValue(G = 1, E = retraction_amount, F = retraction_speed * 60) + "\n"
+
+ # Optionally extrude material
+ if extrude_amount != 0:
+ prepend_gcode += self.putValue(G = 1, E = extrude_amount, F = extrude_speed * 60) + "\n"
+
+ # and retract again, the properly primes the nozzle
+ # when changing filament.
+ if retraction_amount != 0:
+ prepend_gcode += self.putValue(G = 1, E = -retraction_amount, F = retraction_speed * 60) + "\n"
+
+ # Move the head back
+ prepend_gcode += self.putValue(G = 1, Z = current_z + 1, F = 300) + "\n"
+ prepend_gcode += self.putValue(G = 1, X = x, Y = y, F = 9000) + "\n"
+ if retraction_amount != 0:
+ prepend_gcode += self.putValue(G = 1, E = retraction_amount, F = retraction_speed * 60) + "\n"
+ prepend_gcode += self.putValue(G = 1, F = 9000) + "\n"
+ prepend_gcode += self.putValue(M = 82) + "\n"
+
+ # reset extrude value to pre pause value
+ prepend_gcode += self.putValue(G = 92, E = current_e) + "\n"
+
+ layer = prepend_gcode + layer
+
+
+ # Override the data of this layer with the
+ # modified data
+ data[index] = layer
+ return data
return data
diff --git a/plugins/PostProcessingPlugin/scripts/PauseAtHeightforRepetier.py b/plugins/PostProcessingPlugin/scripts/PauseAtHeightforRepetier.py
index 710baab26a..f6c93d9ae6 100644
--- a/plugins/PostProcessingPlugin/scripts/PauseAtHeightforRepetier.py
+++ b/plugins/PostProcessingPlugin/scripts/PauseAtHeightforRepetier.py
@@ -35,7 +35,7 @@ class PauseAtHeightforRepetier(Script):
"type": "float",
"default_value": 5.0
},
- "head_move_Z":
+ "head_move_Z":
{
"label": "Head move Z",
"description": "The Hieght of Z-axis retraction before parking.",
diff --git a/plugins/PostProcessingPlugin/scripts/Stretch.py b/plugins/PostProcessingPlugin/scripts/Stretch.py
index bcb923d3ff..c7a36ab7d6 100644
--- a/plugins/PostProcessingPlugin/scripts/Stretch.py
+++ b/plugins/PostProcessingPlugin/scripts/Stretch.py
@@ -12,6 +12,7 @@ import numpy as np
from UM.Logger import Logger
from UM.Application import Application
import re
+from cura.Settings.ExtruderManager import ExtruderManager
def _getValue(line, key, default=None):
"""
@@ -90,9 +91,9 @@ class Stretcher():
"""
Computes the new X and Y coordinates of all g-code steps
"""
- Logger.log("d", "Post stretch with line width = " + str(self.line_width)
- + "mm wide circle stretch = " + str(self.wc_stretch)+ "mm"
- + "and push wall stretch = " + str(self.pw_stretch) + "mm")
+ Logger.log("d", "Post stretch with line width " + str(self.line_width)
+ + "mm wide circle stretch " + str(self.wc_stretch)+ "mm"
+ + " and push wall stretch " + str(self.pw_stretch) + "mm")
retdata = []
layer_steps = []
current = GCodeStep(0)
@@ -282,7 +283,7 @@ class Stretcher():
dmin_tri is the minimum distance between two consecutive points
of an acceptable triangle
"""
- dmin_tri = self.line_width / 2.0
+ dmin_tri = 0.5
iextra_base = np.floor_divide(len(orig_seq), 3) # Nb of extra points
ibeg = 0 # Index of first point of the triangle
iend = 0 # Index of the third point of the triangle
@@ -325,9 +326,10 @@ class Stretcher():
relpos = 0.5 # To avoid division by zero or precision loss
projection = (pos_before[ibeg] + relpos * (pos_after[iend] - pos_before[ibeg]))
dist_from_proj = np.sqrt(((projection - step) ** 2).sum(0))
- if dist_from_proj > 0.001: # Move central point only if points are not aligned
+ if dist_from_proj > 0.0003: # Move central point only if points are not aligned
modif_seq[i] = (step - (self.wc_stretch / dist_from_proj)
* (projection - step))
+
return
def wideTurn(self, orig_seq, modif_seq):
@@ -411,8 +413,6 @@ class Stretcher():
modif_seq[ibeg] = modif_seq[ibeg] + xperp * self.pw_stretch
elif not materialleft and materialright:
modif_seq[ibeg] = modif_seq[ibeg] - xperp * self.pw_stretch
- if materialleft and materialright:
- modif_seq[ibeg] = orig_seq[ibeg] # Surrounded by walls, don't move
# Setup part of the stretch plugin
class Stretch(Script):
@@ -437,7 +437,7 @@ class Stretch(Script):
"description": "Distance by which the points are moved by the correction effect in corners. The higher this value, the higher the effect",
"unit": "mm",
"type": "float",
- "default_value": 0.08,
+ "default_value": 0.1,
"minimum_value": 0,
"minimum_value_warning": 0,
"maximum_value_warning": 0.2
@@ -448,7 +448,7 @@ class Stretch(Script):
"description": "Distance by which the points are moved by the correction effect when two lines are nearby. The higher this value, the higher the effect",
"unit": "mm",
"type": "float",
- "default_value": 0.08,
+ "default_value": 0.1,
"minimum_value": 0,
"minimum_value_warning": 0,
"maximum_value_warning": 0.2
@@ -463,7 +463,7 @@ class Stretch(Script):
the returned string is the list of modified g-code instructions
"""
stretcher = Stretcher(
- Application.getInstance().getGlobalContainerStack().getProperty("line_width", "value")
+ ExtruderManager.getInstance().getActiveExtruderStack().getProperty("machine_nozzle_size", "value")
, self.getSettingValueByKey("wc_stretch"), self.getSettingValueByKey("pw_stretch"))
return stretcher.execute(data)
diff --git a/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py b/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py
index ff930e2c31..c81e4a76bc 100644
--- a/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py
+++ b/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2016 Ultimaker B.V.
+# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os.path
@@ -7,7 +7,7 @@ from UM.Application import Application
from UM.Logger import Logger
from UM.Message import Message
from UM.FileHandler.WriteFileJob import WriteFileJob
-from UM.Mesh.MeshWriter import MeshWriter
+from UM.FileHandler.FileWriter import FileWriter #To check against the write modes (text vs. binary).
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from UM.OutputDevice.OutputDevice import OutputDevice
from UM.OutputDevice import OutputDeviceError
@@ -39,7 +39,7 @@ class RemovableDriveOutputDevice(OutputDevice):
# MIME types available to the currently active machine?
#
def requestWrite(self, nodes, file_name = None, filter_by_machine = False, file_handler = None, **kwargs):
- filter_by_machine = True # This plugin is indended to be used by machine (regardless of what it was told to do)
+ filter_by_machine = True # This plugin is intended to be used by machine (regardless of what it was told to do)
if self._writing:
raise OutputDeviceError.DeviceBusyError()
@@ -56,19 +56,21 @@ class RemovableDriveOutputDevice(OutputDevice):
machine_file_formats = [file_type.strip() for file_type in container.getMetaDataEntry("file_formats").split(";")]
# Take the intersection between file_formats and machine_file_formats.
- file_formats = list(filter(lambda file_format: file_format["mime_type"] in machine_file_formats, file_formats))
+ format_by_mimetype = {format["mime_type"]: format for format in file_formats}
+ file_formats = [format_by_mimetype[mimetype] for mimetype in machine_file_formats] #Keep them ordered according to the preference in machine_file_formats.
if len(file_formats) == 0:
Logger.log("e", "There are no file formats available to write with!")
- raise OutputDeviceError.WriteRequestFailedError(catalog.i18nc("There are no file formats available to write with!"))
+ raise OutputDeviceError.WriteRequestFailedError(catalog.i18nc("@info:status", "There are no file formats available to write with!"))
+ preferred_format = file_formats[0]
# Just take the first file format available.
if file_handler is not None:
- writer = file_handler.getWriterByMimeType(file_formats[0]["mime_type"])
+ writer = file_handler.getWriterByMimeType(preferred_format["mime_type"])
else:
- writer = Application.getInstance().getMeshFileHandler().getWriterByMimeType(file_formats[0]["mime_type"])
+ writer = Application.getInstance().getMeshFileHandler().getWriterByMimeType(preferred_format["mime_type"])
- extension = file_formats[0]["extension"]
+ extension = preferred_format["extension"]
if file_name is None:
file_name = self._automaticFileName(nodes)
@@ -80,8 +82,11 @@ class RemovableDriveOutputDevice(OutputDevice):
try:
Logger.log("d", "Writing to %s", file_name)
# Using buffering greatly reduces the write time for many lines of gcode
- self._stream = open(file_name, "wt", buffering = 1, encoding = "utf-8")
- job = WriteFileJob(writer, self._stream, nodes, MeshWriter.OutputMode.TextMode)
+ if preferred_format["mode"] == FileWriter.OutputMode.TextMode:
+ self._stream = open(file_name, "wt", buffering = 1, encoding = "utf-8")
+ else: #Binary mode.
+ self._stream = open(file_name, "wb", buffering = 1)
+ job = WriteFileJob(writer, self._stream, nodes, preferred_format["mode"])
job.setFileName(file_name)
job.progress.connect(self._onProgress)
job.finished.connect(self._onFinished)
diff --git a/plugins/SimulationView/SimulationPass.py b/plugins/SimulationView/SimulationPass.py
index 76d7127534..cd0eda2929 100644
--- a/plugins/SimulationView/SimulationPass.py
+++ b/plugins/SimulationView/SimulationPass.py
@@ -93,7 +93,7 @@ class SimulationPass(RenderPass):
self.bind()
tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Overlay, backface_cull = True)
- active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
+ active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
head_position = None # Indicates the current position of the print head
nozzle_node = None
diff --git a/plugins/SimulationView/SimulationSliderLabel.qml b/plugins/SimulationView/SimulationSliderLabel.qml
index 1c8daf867f..6f7749df63 100644
--- a/plugins/SimulationView/SimulationSliderLabel.qml
+++ b/plugins/SimulationView/SimulationSliderLabel.qml
@@ -49,7 +49,7 @@ UM.PointingRectangle {
anchors {
left: parent.left
- leftMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2)
+ leftMargin: Math.round(UM.Theme.getSize("default_margin").width / 2)
verticalCenter: parent.verticalCenter
}
@@ -91,7 +91,7 @@ UM.PointingRectangle {
anchors {
left: parent.right
- leftMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2)
+ leftMargin: Math.round(UM.Theme.getSize("default_margin").width / 2)
verticalCenter: parent.verticalCenter
}
diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py
index 253ece315e..3697e38661 100644
--- a/plugins/SimulationView/SimulationView.py
+++ b/plugins/SimulationView/SimulationView.py
@@ -74,7 +74,7 @@ class SimulationView(View):
self._global_container_stack = None
self._proxy = SimulationViewProxy()
- self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged)
+ self._controller.getScene().sceneChanged.connect(self._onSceneChanged)
self._resetSettings()
self._legend_items = None
@@ -158,9 +158,12 @@ class SimulationView(View):
return self._nozzle_node
def _onSceneChanged(self, node):
- self.setActivity(False)
- self.calculateMaxLayers()
- self.calculateMaxPathsOnLayer(self._current_layer_num)
+ if node.getMeshData() is None:
+ self.resetLayerData()
+ else:
+ self.setActivity(False)
+ self.calculateMaxLayers()
+ self.calculateMaxPathsOnLayer(self._current_layer_num)
def isBusy(self):
return self._busy
@@ -342,6 +345,11 @@ class SimulationView(View):
min_layer_number = sys.maxsize
max_layer_number = -sys.maxsize
for layer_id in layer_data.getLayers():
+
+ # If a layer doesn't contain any polygons, skip it (for infill meshes taller than print objects
+ if len(layer_data.getLayer(layer_id).polygons) < 1:
+ continue
+
# Store the max and min feedrates and thicknesses for display purposes
for p in layer_data.getLayer(layer_id).polygons:
self._max_feedrate = max(float(p.lineFeedrates.max()), self._max_feedrate)
@@ -634,4 +642,3 @@ class _CreateTopLayersJob(Job):
def cancel(self):
self._cancel = True
super().cancel()
-
diff --git a/plugins/SimulationView/SimulationView.qml b/plugins/SimulationView/SimulationView.qml
index db92fac798..6aad413f9b 100644
--- a/plugins/SimulationView/SimulationView.qml
+++ b/plugins/SimulationView/SimulationView.qml
@@ -61,7 +61,7 @@ Item
Button {
id: collapseButton
anchors.top: parent.top
- anchors.topMargin: Math.floor(UM.Theme.getSize("default_margin").height + (UM.Theme.getSize("layerview_row").height - UM.Theme.getSize("default_margin").height) / 2)
+ anchors.topMargin: Math.round(UM.Theme.getSize("default_margin").height + (UM.Theme.getSize("layerview_row").height - UM.Theme.getSize("default_margin").height) / 2)
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
@@ -193,7 +193,7 @@ Item
Item
{
- height: Math.floor(UM.Theme.getSize("default_margin").width / 2)
+ height: Math.round(UM.Theme.getSize("default_margin").width / 2)
width: width
}
@@ -231,7 +231,7 @@ Item
width: UM.Theme.getSize("layerview_legend_size").width
height: UM.Theme.getSize("layerview_legend_size").height
color: model.color
- radius: width / 2
+ radius: Math.round(width / 2)
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
visible: !viewSettings.show_legend & !viewSettings.show_gradient
@@ -249,7 +249,7 @@ Item
anchors.verticalCenter: parent.verticalCenter
anchors.left: extrudersModelCheckBox.left;
anchors.right: extrudersModelCheckBox.right;
- anchors.leftMargin: UM.Theme.getSize("checkbox").width + UM.Theme.getSize("default_margin").width /2
+ anchors.leftMargin: UM.Theme.getSize("checkbox").width + Math.round(UM.Theme.getSize("default_margin").width/2)
anchors.rightMargin: UM.Theme.getSize("default_margin").width * 2
}
}
@@ -316,7 +316,7 @@ Item
anchors.verticalCenter: parent.verticalCenter
anchors.left: legendModelCheckBox.left;
anchors.right: legendModelCheckBox.right;
- anchors.leftMargin: UM.Theme.getSize("checkbox").width + UM.Theme.getSize("default_margin").width /2
+ anchors.leftMargin: UM.Theme.getSize("checkbox").width + Math.round(UM.Theme.getSize("default_margin").width/2)
anchors.rightMargin: UM.Theme.getSize("default_margin").width * 2
}
}
@@ -461,7 +461,7 @@ Item
visible: viewSettings.show_feedrate_gradient
anchors.left: parent.right
height: parent.width
- width: UM.Theme.getSize("layerview_row").height * 1.5
+ width: Math.round(UM.Theme.getSize("layerview_row").height * 1.5)
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
transform: Rotation {origin.x: 0; origin.y: 0; angle: 90}
@@ -492,7 +492,7 @@ Item
visible: viewSettings.show_thickness_gradient
anchors.left: parent.right
height: parent.width
- width: UM.Theme.getSize("layerview_row").height * 1.5
+ width: Math.round(UM.Theme.getSize("layerview_row").height * 1.5)
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
transform: Rotation {origin.x: 0; origin.y: 0; angle: 90}
diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py
index 971a324aa2..e1c990c596 100755
--- a/plugins/SliceInfoPlugin/SliceInfo.py
+++ b/plugins/SliceInfoPlugin/SliceInfo.py
@@ -146,7 +146,7 @@ class SliceInfo(Extension):
model_stack = node.callDecoration("getStack")
if model_stack:
model_settings["support_enabled"] = model_stack.getProperty("support_enable", "value")
- model_settings["support_extruder_nr"] = int(model_stack.getProperty("support_extruder_nr", "value"))
+ model_settings["support_extruder_nr"] = int(model_stack.getExtruderPositionValueWithDefault("support_extruder_nr"))
# Mesh modifiers;
model_settings["infill_mesh"] = model_stack.getProperty("infill_mesh", "value")
@@ -177,7 +177,7 @@ class SliceInfo(Extension):
# Support settings
print_settings["support_enabled"] = global_container_stack.getProperty("support_enable", "value")
- print_settings["support_extruder_nr"] = int(global_container_stack.getProperty("support_extruder_nr", "value"))
+ print_settings["support_extruder_nr"] = int(global_container_stack.getExtruderPositionValueWithDefault("support_extruder_nr"))
# Platform adhesion settings
print_settings["adhesion_type"] = global_container_stack.getProperty("adhesion_type", "value")
diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py
index 50ff2864b7..ff5aab96d2 100644
--- a/plugins/SolidView/SolidView.py
+++ b/plugins/SolidView/SolidView.py
@@ -62,7 +62,7 @@ class SolidView(View):
global_container_stack = Application.getInstance().getGlobalContainerStack()
if global_container_stack:
- support_extruder_nr = global_container_stack.getProperty("support_extruder_nr", "value")
+ support_extruder_nr = global_container_stack.getExtruderPositionValueWithDefault("support_extruder_nr")
support_angle_stack = Application.getInstance().getExtruderManager().getExtruderStack(support_extruder_nr)
if support_angle_stack is not None and Preferences.getInstance().getValue("view/show_overhang"):
@@ -78,22 +78,18 @@ class SolidView(View):
for node in DepthFirstIterator(scene.getRoot()):
if not node.render(renderer):
- if node.getMeshData() and node.isVisible():
+ if node.getMeshData() and node.isVisible() and not node.callDecoration("getLayerData"):
uniforms = {}
shade_factor = 1.0
per_mesh_stack = node.callDecoration("getStack")
- # Get color to render this mesh in from ExtrudersModel
- extruder_index = 0
- extruder_id = node.callDecoration("getActiveExtruder")
- if extruder_id:
- extruder_index = max(0, self._extruders_model.find("id", extruder_id))
+ extruder_index = int(node.callDecoration("getActiveExtruderPosition"))
# Use the support extruder instead of the active extruder if this is a support_mesh
if per_mesh_stack:
if per_mesh_stack.getProperty("support_mesh", "value"):
- extruder_index = int(global_container_stack.getProperty("support_extruder_nr", "value"))
+ extruder_index = int(global_container_stack.getExtruderPositionValueWithDefault("support_extruder_nr"))
try:
material_color = self._extruders_model.getItem(extruder_index)["color"]
diff --git a/plugins/SupportEraser/SupportEraser.py b/plugins/SupportEraser/SupportEraser.py
index 9e2d41014d..7884ca30c7 100644
--- a/plugins/SupportEraser/SupportEraser.py
+++ b/plugins/SupportEraser/SupportEraser.py
@@ -1,39 +1,101 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
-from UM.Tool import Tool
-from PyQt5.QtCore import Qt, QUrl
-from UM.Application import Application
-from UM.Event import Event
-from UM.Mesh.MeshBuilder import MeshBuilder
-from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
-from UM.Settings.SettingInstance import SettingInstance
-from cura.Scene.CuraSceneNode import CuraSceneNode
-from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
-from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
-from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
-
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.PickingPass import PickingPass
+
+from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
+from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
+
+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
+
class SupportEraser(Tool):
def __init__(self):
super().__init__()
self._shortcut_key = Qt.Key_G
- self._controller = Application.getInstance().getController()
+ self._controller = self.getController()
+
+ self._selection_pass = None
+ Application.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
+ # toolbar will have been disabled. That is why we need to ignore the first press event
+ # after the selection has been cleared.
+ Selection.selectionChanged.connect(self._onSelectionChanged)
+ self._had_selection = False
+ self._skip_press = False
+
+ self._had_selection_timer = QTimer()
+ self._had_selection_timer.setInterval(0)
+ self._had_selection_timer.setSingleShot(True)
+ self._had_selection_timer.timeout.connect(self._selectionChangeDelay)
def event(self, event):
super().event(event)
+ modifiers = QApplication.keyboardModifiers()
+ ctrl_is_active = modifiers & Qt.ControlModifier
- if event.type == Event.ToolActivateEvent:
+ if event.type == Event.MousePressEvent and self._controller.getToolsEnabled():
+ if ctrl_is_active:
+ self._controller.setActiveTool("TranslateTool")
+ return
- # Load the remover mesh:
- self._createEraserMesh()
+ if self._skip_press:
+ # The selection was previously cleared, do not add/remove an anti-support mesh but
+ # use this click for selection and reactivating this tool only.
+ self._skip_press = False
+ return
- # After we load the mesh, deactivate the tool again:
- self.getController().setActiveTool(None)
+ if self._selection_pass is None:
+ # The selection renderpass is used to identify objects in the current view
+ self._selection_pass = Application.getInstance().getRenderer().getRenderPass("selection")
+ picked_node = self._controller.getScene().findObject(self._selection_pass.getIdAtPosition(event.x, event.y))
+ if not picked_node:
+ # There is no slicable object at the picked location
+ return
- def _createEraserMesh(self):
+ node_stack = picked_node.callDecoration("getStack")
+ if node_stack:
+ if node_stack.getProperty("anti_overhang_mesh", "value"):
+ self._removeEraserMesh(picked_node)
+ return
+
+ elif node_stack.getProperty("support_mesh", "value") or node_stack.getProperty("infill_mesh", "value") or node_stack.getProperty("cutting_mesh", "value"):
+ # Only "normal" meshes can have anti_overhang_meshes added to them
+ return
+
+ # Create a pass for picking a world-space location from the mouse location
+ active_camera = self._controller.getScene().getActiveCamera()
+ picking_pass = PickingPass(active_camera.getViewportWidth(), active_camera.getViewportHeight())
+ picking_pass.render()
+
+ picked_position = picking_pass.getPickedPosition(event.x, event.y)
+
+ # Add the anti_overhang_mesh cube at the picked location
+ self._createEraserMesh(picked_node, picked_position)
+
+ def _createEraserMesh(self, parent: CuraSceneNode, position: Vector):
node = CuraSceneNode()
node.setName("Eraser")
@@ -42,27 +104,60 @@ class SupportEraser(Tool):
mesh.addCube(10,10,10)
node.setMeshData(mesh.build())
- active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
-
- node.addDecorator(SettingOverrideDecorator())
+ active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
node.addDecorator(BuildPlateDecorator(active_build_plate))
node.addDecorator(SliceableObjectDecorator())
- stack = node.callDecoration("getStack") #Don't try to get the active extruder since it may be None anyway.
- if not stack:
- node.addDecorator(SettingOverrideDecorator())
- stack = node.callDecoration("getStack")
-
+ stack = node.callDecoration("getStack") # created by SettingOverrideDecorator that is automatically added to CuraSceneNode
settings = stack.getTop()
- if not (settings.getInstance("anti_overhang_mesh") and settings.getProperty("anti_overhang_mesh", "value")):
- definition = stack.getSettingDefinition("anti_overhang_mesh")
- new_instance = SettingInstance(definition, settings)
- new_instance.setProperty("value", True)
- new_instance.resetState() # Ensure that the state is not seen as a user state.
- settings.addInstance(new_instance)
+ definition = stack.getSettingDefinition("anti_overhang_mesh")
+ new_instance = SettingInstance(definition, settings)
+ new_instance.setProperty("value", True)
+ new_instance.resetState() # Ensure that the state is not seen as a user state.
+ settings.addInstance(new_instance)
- scene = self._controller.getScene()
- op = AddSceneNodeOperation(node, scene.getRoot())
+ op = AddSceneNodeOperation(node, parent)
op.push()
+ node.setPosition(position, CuraSceneNode.TransformSpace.World)
+
Application.getInstance().getController().getScene().sceneChanged.emit(node)
+
+ def _removeEraserMesh(self, node: CuraSceneNode):
+ parent = node.getParent()
+ if parent == self._controller.getScene().getRoot():
+ parent = None
+
+ op = RemoveSceneNodeOperation(node)
+ op.push()
+
+ if parent and not Selection.isSelected(parent):
+ Selection.add(parent)
+
+ Application.getInstance().getController().getScene().sceneChanged.emit(node)
+
+ def _updateEnabled(self):
+ plugin_enabled = False
+
+ global_container_stack = Application.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)
+
+ def _onSelectionChanged(self):
+ # When selection is passed from one object to another object, first the selection is cleared
+ # and then it is set to the new object. We are only interested in the change from no selection
+ # to a selection or vice-versa, not in a change from one object to another. A timer is used to
+ # "merge" a possible clear/select action in a single frame
+ if Selection.hasSelection() != self._had_selection:
+ self._had_selection_timer.start()
+
+ def _selectionChangeDelay(self):
+ has_selection = Selection.hasSelection()
+ if not has_selection and self._had_selection:
+ self._skip_press = True
+ else:
+ self._skip_press = False
+
+ self._had_selection = has_selection
diff --git a/plugins/UFPWriter/UFPWriter.py b/plugins/UFPWriter/UFPWriter.py
index 0f49a30403..aca293e25a 100644
--- a/plugins/UFPWriter/UFPWriter.py
+++ b/plugins/UFPWriter/UFPWriter.py
@@ -23,7 +23,7 @@ class UFPWriter(MeshWriter):
def _createSnapshot(self, *args):
# must be called from the main thread because of OpenGL
Logger.log("d", "Creating thumbnail image...")
- self._snapshot = Snapshot.snapshot()
+ self._snapshot = Snapshot.snapshot(width = 300, height = 300)
def write(self, stream, nodes, mode = MeshWriter.OutputMode.BinaryMode):
archive = VirtualFile()
diff --git a/plugins/UFPWriter/__init__.py b/plugins/UFPWriter/__init__.py
index 77f8e81222..9db6b042f8 100644
--- a/plugins/UFPWriter/__init__.py
+++ b/plugins/UFPWriter/__init__.py
@@ -1,13 +1,23 @@
#Copyright (c) 2018 Ultimaker B.V.
#Cura is released under the terms of the LGPLv3 or higher.
-from . import UFPWriter
+import sys
+
+from UM.Logger import Logger
+try:
+ from . import UFPWriter
+except ImportError:
+ Logger.log("w", "Could not import UFPWriter; libCharon may be missing")
+
from UM.i18n import i18nCatalog #To translate the file format description.
from UM.Mesh.MeshWriter import MeshWriter #For the binary mode flag.
i18n_catalog = i18nCatalog("cura")
def getMetaData():
+ if "UFPWriter.UFPWriter" not in sys.modules:
+ return {}
+
return {
"mesh_writer": {
"output": [
@@ -22,4 +32,7 @@ def getMetaData():
}
def register(app):
+ if "UFPWriter.UFPWriter" not in sys.modules:
+ return {}
+
return { "mesh_writer": UFPWriter.UFPWriter() }
diff --git a/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py
index 8f9a4d1d8f..f3667fc2f3 100644
--- a/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py
+++ b/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py
@@ -1,12 +1,17 @@
-# Copyright (c) 2017 Ultimaker B.V.
+# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
+from UM.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
from UM.Qt.Duration import Duration, DurationFormat
+from UM.OutputDevice import OutputDeviceError #To show that something went wrong when writing.
+from UM.Scene.SceneNode import SceneNode #For typing.
+from UM.Version import Version #To check against firmware versions for support.
from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
@@ -20,10 +25,11 @@ from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QObject
-from time import time
+from time import time, sleep
from datetime import datetime
-from typing import Optional
+from typing import Optional, Dict, List
+import io #To create the correct buffers for sending data to the printer.
import json
import os
@@ -77,26 +83,49 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._cluster_size = int(properties.get(b"cluster_size", 0))
- def requestWrite(self, nodes, file_name=None, filter_by_machine=False, file_handler=None, **kwargs):
+ self._latest_reply_handler = None
+
+ def requestWrite(self, nodes: List[SceneNode], file_name=None, filter_by_machine=False, file_handler=None, **kwargs):
self.writeStarted.emit(self)
- gcode_dict = getattr(Application.getInstance().getController().getScene(), "gcode_dict", [])
- active_build_plate_id = Application.getInstance().getBuildPlateModel().activeBuildPlate
- gcode_list = gcode_dict[active_build_plate_id]
-
- if not gcode_list:
- # Unable to find g-code. Nothing to send
- return
-
- self._gcode = gcode_list
-
- if len(self._printers) > 1:
- self._spawnPrinterSelectionDialog()
+ #Formats supported by this application (file types that we can actually write).
+ if file_handler:
+ file_formats = file_handler.getSupportedFileTypesWrite()
else:
- self.sendPrintJob()
+ file_formats = Application.getInstance().getMeshFileHandler().getSupportedFileTypesWrite()
- # Notify the UI that a switch to the print monitor should happen
- Application.getInstance().getController().setActiveStage("MonitorStage")
+ #Create a list from the supported file formats string.
+ machine_file_formats = Application.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"):
+ machine_file_formats = ["application/x-ufp"] + machine_file_formats
+
+ # Take the intersection between file_formats and machine_file_formats.
+ format_by_mimetype = {format["mime_type"]: format for format in file_formats}
+ file_formats = [format_by_mimetype[mimetype] for mimetype in machine_file_formats] #Keep them ordered according to the preference in machine_file_formats.
+
+ if len(file_formats) == 0:
+ Logger.log("e", "There are no file formats available to write with!")
+ raise OutputDeviceError.WriteRequestFailedError(i18n_catalog.i18nc("@info:status", "There are no file formats available to write with!"))
+ preferred_format = file_formats[0]
+
+ #Just take the first file format available.
+ if file_handler is not None:
+ writer = file_handler.getWriterByMimeType(preferred_format["mime_type"])
+ else:
+ writer = Application.getInstance().getMeshFileHandler().getWriterByMimeType(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)
+ self._sending_job.send(None) #Start the generator.
+
+ if len(self._printers) > 1: #We need to ask the user.
+ self._spawnPrinterSelectionDialog()
+ is_job_sent = True
+ else: #Just immediately continue.
+ self._sending_job.send("") #No specifically selected printer.
+ is_job_sent = self._sending_job.send(None)
def _spawnPrinterSelectionDialog(self):
if self._printer_selection_dialog is None:
@@ -109,29 +138,54 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
def clusterSize(self):
return self._cluster_size
- @pyqtSlot()
+ ## Allows the user to choose a printer to print with from the printer
+ # selection dialogue.
+ # \param target_printer The name of the printer to target.
@pyqtSlot(str)
- def sendPrintJob(self, target_printer = ""):
+ def selectPrinter(self, target_printer: str = "") -> None:
+ self._sending_job.send(target_printer)
+
+ ## Greenlet to send a job to the printer over the network.
+ #
+ # This greenlet gets called asynchronously in requestWrite. It is a
+ # greenlet in order to optionally wait for selectPrinter() to select a
+ # printer.
+ # The greenlet yields exactly three times: First time None,
+ # \param writer The file writer to use to create the data.
+ # \param preferred_format A dictionary containing some information about
+ # what format to write to. This is necessary to create the correct buffer
+ # types and file extension and such.
+ def _sendPrintJob(self, writer: FileWriter, preferred_format: Dict, nodes: List[SceneNode]):
Logger.log("i", "Sending print job to printer.")
if self._sending_gcode:
self._error_message = Message(
i18n_catalog.i18nc("@info:status",
"Sending new jobs (temporarily) blocked, still sending the previous print job."))
self._error_message.show()
- return
+ yield #Wait on the user to select a target printer.
+ yield #Wait for the write job to be finished.
+ yield False #Return whether this was a success or not.
+ yield #Prevent StopIteration.
self._sending_gcode = True
- self._progress_message = Message(i18n_catalog.i18nc("@info:status", "Sending data to printer"), 0, False, -1,
- i18n_catalog.i18nc("@info:title", "Sending Data"))
- self._progress_message.addAction("Abort", i18n_catalog.i18nc("@action:button", "Cancel"), None, "")
+ 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
+ if preferred_format["mode"] == FileWriter.OutputMode.TextMode:
+ stream = io.StringIO()
+ else: #Binary mode.
+ stream = io.BytesIO()
+
+ job = WriteFileJob(writer, stream, nodes, preferred_format["mode"])
+
+ self._progress_message = Message(i18n_catalog.i18nc("@info:status", "Sending data to printer"), lifetime = 0, dismissable = False, progress = -1,
+ title = i18n_catalog.i18nc("@info:title", "Sending Data"))
+ self._progress_message.addAction("Abort", i18n_catalog.i18nc("@action:button", "Cancel"), icon = None, description = "")
self._progress_message.actionTriggered.connect(self._progressMessageActionTriggered)
self._progress_message.show()
- compressed_gcode = self._compressGCode()
- if compressed_gcode is None:
- # Abort was called.
- return
+ job.start()
parts = []
@@ -143,18 +197,27 @@ 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 = "%s.gcode.gz" % Application.getInstance().getPrintInformation().jobName
+ file_name = Application.getInstance().getPrintInformation().jobName + "." + preferred_format["extension"]
- parts.append(self._createFormPart("name=\"file\"; filename=\"%s\"" % file_name, compressed_gcode))
+ while not job.isFinished():
+ sleep(0.1)
+ output = stream.getvalue() #Either str or bytes depending on the output mode.
+ if isinstance(stream, io.StringIO):
+ output = output.encode("utf-8")
- self.postFormWithParts("print_jobs/", parts, onFinished=self._onPostPrintJobFinished, onProgress=self._onUploadPrintJobProgress)
+ 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)
+
+ yield True #Return that we had success!
+ yield #To prevent having to catch the StopIteration exception.
@pyqtProperty(QObject, notify=activePrinterChanged)
- def activePrinter(self) -> Optional["PrinterOutputModel"]:
+ def activePrinter(self) -> Optional[PrinterOutputModel]:
return self._active_printer
@pyqtSlot(QObject)
- def setActivePrinter(self, printer):
+ def setActivePrinter(self, printer: Optional[PrinterOutputModel]):
if self._active_printer != printer:
if self._active_printer and self._active_printer.camera:
self._active_printer.camera.stop()
@@ -166,7 +229,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._compressing_gcode = False
self._sending_gcode = False
- def _onUploadPrintJobProgress(self, bytes_sent, bytes_total):
+ def _onUploadPrintJobProgress(self, bytes_sent:int, bytes_total:int):
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
@@ -175,11 +238,24 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
if 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)
+
+ # If successfully sent:
+ if bytes_sent == bytes_total:
+ # Show a confirmation to the user so they know the job was sucessful and provide the option to switch to the
+ # monitor tab.
+ self._success_message = Message(
+ i18n_catalog.i18nc("@info:status", "Print job was successfully sent to the printer."),
+ lifetime=5, dismissable=True,
+ title=i18n_catalog.i18nc("@info:title", "Data Sent"))
+ self._success_message.addAction("View", i18n_catalog.i18nc("@action:button", "View in Montior"), icon=None,
+ description="")
+ self._success_message.actionTriggered.connect(self._successMessageActionTriggered)
+ self._success_message.show()
else:
self._progress_message.setProgress(0)
self._progress_message.hide()
- def _progressMessageActionTriggered(self, message_id=None, action_id=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()
@@ -187,30 +263,40 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._sending_gcode = False
Application.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
+ if self._latest_reply_handler:
+ self._latest_reply_handler.disconnect()
+ self._latest_reply_handler = None
+
+ def _successMessageActionTriggered(self, message_id: Optional[str]=None, action_id: Optional[str]=None) -> None:
+ if action_id == "View":
+ Application.getInstance().getController().setActiveStage("MonitorStage")
+
@pyqtSlot()
- def openPrintJobControlPanel(self):
+ def openPrintJobControlPanel(self) -> None:
Logger.log("d", "Opening print job control panel...")
QDesktopServices.openUrl(QUrl("http://" + self._address + "/print_jobs"))
@pyqtSlot()
- def openPrinterControlPanel(self):
+ def openPrinterControlPanel(self) -> None:
Logger.log("d", "Opening printer control panel...")
QDesktopServices.openUrl(QUrl("http://" + self._address + "/printers"))
@pyqtProperty("QVariantList", notify=printJobsChanged)
- def printJobs(self):
+ def printJobs(self)-> List[PrintJobOutputModel] :
return self._print_jobs
@pyqtProperty("QVariantList", notify=printJobsChanged)
- def queuedPrintJobs(self):
- return [print_job for print_job in self._print_jobs if print_job.assignedPrinter is None]
+ def queuedPrintJobs(self) -> List[PrintJobOutputModel]:
+ return [print_job for print_job in self._print_jobs if print_job.state == "queued"]
@pyqtProperty("QVariantList", notify=printJobsChanged)
- def activePrintJobs(self):
- return [print_job for print_job in self._print_jobs if print_job.assignedPrinter is not None]
+ 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):
+ def connectedPrintersTypeCount(self) -> List[PrinterOutputModel]:
printer_count = {}
for printer in self._printers:
if printer.type in printer_count:
@@ -223,22 +309,22 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
return result
@pyqtSlot(int, result=str)
- def formatDuration(self, seconds):
+ def formatDuration(self, seconds: int) -> str:
return Duration(seconds).getDisplayString(DurationFormat.Format.Short)
@pyqtSlot(int, result=str)
- def getTimeCompleted(self, time_remaining):
+ 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)
- def getDateCompleted(self, time_remaining):
+ def getDateCompleted(self, time_remaining: int) -> str:
current_time = time()
datetime_completed = datetime.fromtimestamp(current_time + time_remaining)
return (datetime_completed.strftime("%a %b ") + "{day}".format(day=datetime_completed.day)).upper()
- def _printJobStateChanged(self):
+ def _printJobStateChanged(self) -> None:
username = self._getUserName()
if username is None:
@@ -261,13 +347,13 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
# Keep a list of all completed jobs so we know if something changed next time.
self._finished_jobs = finished_jobs
- def _update(self):
+ def _update(self) -> None:
if not super()._update():
return
self.get("printers/", onFinished=self._onGetPrintersDataFinished)
self.get("print_jobs/", onFinished=self._onGetPrintJobsFinished)
- def _onGetPrintJobsFinished(self, reply: QNetworkReply):
+ def _onGetPrintJobsFinished(self, reply: QNetworkReply) -> None:
if not checkValidGetReply(reply):
return
@@ -287,8 +373,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._updatePrintJob(print_job, print_job_data)
if print_job.state != "queued": # Print job should be assigned to a printer.
- if print_job.state == "failed":
- # Print job was failed, so don't attach it to a printer.
+ if print_job.state in ["failed", "finished", "aborted"]:
+ # Print job was already completed, so don't attach it to a printer.
printer = None
else:
printer = self._getPrinterByKey(print_job_data["printer_uuid"])
@@ -309,7 +395,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
if job_list_changed:
self.printJobsChanged.emit() # Do a single emit for all print job changes.
- def _onGetPrintersDataFinished(self, reply: QNetworkReply):
+ def _onGetPrintersDataFinished(self, reply: QNetworkReply) -> None:
if not checkValidGetReply(reply):
return
@@ -338,34 +424,45 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
if removed_printers or printer_list_changed:
self.printersChanged.emit()
- def _createPrinterModel(self, data):
+ def _createPrinterModel(self, data: Dict) -> 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):
+ def _createPrintJobModel(self, data: Dict) -> 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, data):
+ def _updatePrintJob(self, print_job: PrintJobOutputModel, data: Dict) -> 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, data):
+ def _updatePrinter(self, printer: PrinterOutputModel, data: Dict) -> 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"]
+ definitions = ContainerRegistry.getInstance().findDefinitionContainers(name = data["machine_variant"])
+ if not definitions:
+ Logger.log("w", "Unable to find definition for machine variant %s", data["machine_variant"])
+ return
+
+ machine_definition = definitions[0]
+
printer.updateName(data["friendly_name"])
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
+ if "build_plate" in data and machine_definition.getMetaDataEntry("has_variant_buildplates", False):
+ printer.updateBuildplateName(data["build_plate"]["type"])
if not data["enabled"]:
printer.updateState("disabled")
else:
@@ -396,13 +493,13 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
color = material_data["color"]
brand = material_data["brand"]
material_type = material_data["material"]
- name = "Unknown"
+ name = "Empty" if material_data["material"] == "empty" else "Unknown"
material = MaterialOutputModel(guid=material_data["guid"], type=material_type,
brand=brand, color=color, name=name)
extruder.updateActiveMaterial(material)
- def _removeJob(self, job):
+ def _removeJob(self, job: PrintJobOutputModel):
if job not in self._print_jobs:
return False
@@ -413,7 +510,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
return True
- def _removePrinter(self, printer):
+ def _removePrinter(self, printer: PrinterOutputModel):
self._printers.remove(printer)
if self._active_printer == printer:
self._active_printer = None
diff --git a/plugins/UM3NetworkPrinting/ClusterUM3PrinterOutputController.py b/plugins/UM3NetworkPrinting/ClusterUM3PrinterOutputController.py
index 4615cd62dc..707443b9ea 100644
--- a/plugins/UM3NetworkPrinting/ClusterUM3PrinterOutputController.py
+++ b/plugins/UM3NetworkPrinting/ClusterUM3PrinterOutputController.py
@@ -13,7 +13,9 @@ class ClusterUM3PrinterOutputController(PrinterOutputController):
def __init__(self, output_device):
super().__init__(output_device)
self.can_pre_heat_bed = False
+ self.can_pre_heat_hotends = False
self.can_control_manually = False
+ self.can_send_raw_gcode = False
def setJobState(self, job: "PrintJobOutputModel", state: str):
data = "{\"action\": \"%s\"}" % state
diff --git a/plugins/UM3NetworkPrinting/DiscoverUM3Action.py b/plugins/UM3NetworkPrinting/DiscoverUM3Action.py
index 0e872fed43..0b8d6e9f53 100644
--- a/plugins/UM3NetworkPrinting/DiscoverUM3Action.py
+++ b/plugins/UM3NetworkPrinting/DiscoverUM3Action.py
@@ -97,6 +97,25 @@ class DiscoverUM3Action(MachineAction):
else:
return []
+ @pyqtSlot(str)
+ def setGroupName(self, group_name):
+ Logger.log("d", "Attempting to set the group name of the active machine to %s", group_name)
+ global_container_stack = Application.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)
+ else:
+ global_container_stack.addMetaDataEntry("connect_group_name", group_name)
+ global_container_stack.addMetaDataEntry("hidden", False)
+
+ if self._network_plugin:
+ # Ensure that the connection states are refreshed.
+ self._network_plugin.reCheckConnections()
+
@pyqtSlot(str)
def setKey(self, key):
Logger.log("d", "Attempting to set the network key of the active machine to %s", key)
@@ -104,11 +123,13 @@ class DiscoverUM3Action(MachineAction):
if global_container_stack:
meta_data = global_container_stack.getMetaData()
if "um_network_key" in meta_data:
+ previous_network_key= meta_data["um_network_key"]
global_container_stack.setMetaDataEntry("um_network_key", key)
# Delete old authentication data.
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)
else:
global_container_stack.addMetaDataEntry("um_network_key", key)
@@ -126,6 +147,10 @@ class DiscoverUM3Action(MachineAction):
return ""
+ @pyqtSlot(str, result = bool)
+ def existsKey(self, key) -> bool:
+ return Application.getInstance().getMachineManager().existNetworkInstances(network_key = key)
+
@pyqtSlot()
def loadConfigurationFromPrinter(self):
machine_manager = Application.getInstance().getMachineManager()
diff --git a/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml
index 0e58d8e991..0aaeef8fbd 100644
--- a/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml
+++ b/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml
@@ -5,6 +5,7 @@ import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.1
+import QtQuick.Dialogs 1.2
Cura.MachineAction
{
@@ -32,14 +33,34 @@ Cura.MachineAction
if(base.selectedDevice && base.completeProperties)
{
var printerKey = base.selectedDevice.key
- if(manager.getStoredKey() != printerKey)
+ var printerName = base.selectedDevice.name // TODO To change when the groups have a name
+ if (manager.getStoredKey() != printerKey)
{
- manager.setKey(printerKey);
- completed();
+ // Check if there is another instance with the same key
+ if (!manager.existsKey(printerKey))
+ {
+ manager.setKey(printerKey)
+ manager.setGroupName(printerName) // TODO To change when the groups have a name
+ completed()
+ }
+ else
+ {
+ existingConnectionDialog.open()
+ }
}
}
}
+ MessageDialog
+ {
+ id: existingConnectionDialog
+ title: catalog.i18nc("@window:title", "Existing Connection")
+ icon: StandardIcon.Information
+ text: catalog.i18nc("@message:text", "This printer/group is already added to Cura. Please select another printer/group.")
+ standardButtons: StandardButton.Ok
+ modality: Qt.ApplicationModal
+ }
+
Column
{
anchors.fill: parent;
@@ -114,7 +135,7 @@ Cura.MachineAction
Column
{
- width: Math.floor(parent.width * 0.5)
+ width: Math.round(parent.width * 0.5)
spacing: UM.Theme.getSize("default_margin").height
ScrollView
@@ -198,7 +219,7 @@ Cura.MachineAction
}
Column
{
- width: Math.floor(parent.width * 0.5)
+ width: Math.round(parent.width * 0.5)
visible: base.selectedDevice ? true : false
spacing: UM.Theme.getSize("default_margin").height
Label
@@ -216,13 +237,13 @@ Cura.MachineAction
columns: 2
Label
{
- width: Math.floor(parent.width * 0.5)
+ width: Math.round(parent.width * 0.5)
wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "Type")
}
Label
{
- width: Math.floor(parent.width * 0.5)
+ width: Math.round(parent.width * 0.5)
wrapMode: Text.WordWrap
text:
{
@@ -247,25 +268,25 @@ Cura.MachineAction
}
Label
{
- width: Math.floor(parent.width * 0.5)
+ width: Math.round(parent.width * 0.5)
wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "Firmware version")
}
Label
{
- width: Math.floor(parent.width * 0.5)
+ width: Math.round(parent.width * 0.5)
wrapMode: Text.WordWrap
text: base.selectedDevice ? base.selectedDevice.firmwareVersion : ""
}
Label
{
- width: Math.floor(parent.width * 0.5)
+ width: Math.round(parent.width * 0.5)
wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "Address")
}
Label
{
- width: Math.floor(parent.width * 0.5)
+ width: Math.round(parent.width * 0.5)
wrapMode: Text.WordWrap
text: base.selectedDevice ? base.selectedDevice.ipAddress : ""
}
@@ -303,7 +324,7 @@ Cura.MachineAction
Button
{
text: catalog.i18nc("@action:button", "Connect")
- enabled: (base.selectedDevice && base.completeProperties) ? true : false
+ enabled: (base.selectedDevice && base.completeProperties && base.selectedDevice.clusterSize > 0) ? true : false
onClicked: connectToPrinter()
}
}
diff --git a/plugins/UM3NetworkPrinting/LegacyUM3OutputDevice.py b/plugins/UM3NetworkPrinting/LegacyUM3OutputDevice.py
index 647a7f822c..42f00beceb 100644
--- a/plugins/UM3NetworkPrinting/LegacyUM3OutputDevice.py
+++ b/plugins/UM3NetworkPrinting/LegacyUM3OutputDevice.py
@@ -184,7 +184,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
self.writeStarted.emit(self)
gcode_dict = getattr(Application.getInstance().getController().getScene(), "gcode_dict", [])
- active_build_plate_id = Application.getInstance().getBuildPlateModel().activeBuildPlate
+ active_build_plate_id = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
gcode_list = gcode_dict[active_build_plate_id]
if not gcode_list:
@@ -419,8 +419,6 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
self._authentication_failed_message.show()
elif status_code == 200:
self.setAuthenticationState(AuthState.Authenticated)
- # Now we know for sure that we are authenticated, send the material profiles to the machine.
- self._sendMaterialProfiles()
def _checkAuthentication(self):
Logger.log("d", "Checking if authentication is correct for id %s and key %s", self._authentication_id, self._getSafeAuthKey())
diff --git a/plugins/UM3NetworkPrinting/LegacyUM3PrinterOutputController.py b/plugins/UM3NetworkPrinting/LegacyUM3PrinterOutputController.py
index 7a0e113d5b..b12a31b6cf 100644
--- a/plugins/UM3NetworkPrinting/LegacyUM3PrinterOutputController.py
+++ b/plugins/UM3NetworkPrinting/LegacyUM3PrinterOutputController.py
@@ -20,6 +20,7 @@ class LegacyUM3PrinterOutputController(PrinterOutputController):
self._preheat_printer = None
self.can_control_manually = False
+ self.can_send_raw_gcode = False
# Are we still waiting for a response about preheat?
# We need this so we can already update buttons, so it feels more snappy.
diff --git a/plugins/UM3NetworkPrinting/PrintCoreConfiguration.qml b/plugins/UM3NetworkPrinting/PrintCoreConfiguration.qml
index 70fa65da5e..267516091b 100644
--- a/plugins/UM3NetworkPrinting/PrintCoreConfiguration.qml
+++ b/plugins/UM3NetworkPrinting/PrintCoreConfiguration.qml
@@ -10,7 +10,7 @@ Item
id: extruderInfo
property var printCoreConfiguration
- width: Math.floor(parent.width / 2)
+ width: Math.round(parent.width / 2)
height: childrenRect.height
Label
{
diff --git a/plugins/UM3NetworkPrinting/PrintWindow.qml b/plugins/UM3NetworkPrinting/PrintWindow.qml
index d84b0f30ec..43afbcdfe0 100644
--- a/plugins/UM3NetworkPrinting/PrintWindow.qml
+++ b/plugins/UM3NetworkPrinting/PrintWindow.qml
@@ -101,7 +101,7 @@ UM.Dialog
enabled: true
onClicked: {
base.visible = false;
- OutputDevice.sendPrintJob(printerSelectionCombobox.model.get(printerSelectionCombobox.currentIndex).key)
+ OutputDevice.selectPrinter(printerSelectionCombobox.model.get(printerSelectionCombobox.currentIndex).key)
// reset to defaults
printerSelectionCombobox.currentIndex = 0
}
diff --git a/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml b/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml
index eb5cbff8e3..0217767a40 100644
--- a/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml
+++ b/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml
@@ -34,17 +34,17 @@ Rectangle
switch (printer.state)
{
case "pre_print":
- return catalog.i18nc("@label", "Preparing to print")
+ return catalog.i18nc("@label:status", "Preparing to print")
case "printing":
return catalog.i18nc("@label:status", "Printing");
case "idle":
return catalog.i18nc("@label:status", "Available");
case "unreachable":
- return catalog.i18nc("@label:MonitorStatus", "Lost connection with the printer");
- case "maintenance": // TODO: new string
- case "unknown":
+ return catalog.i18nc("@label:status", "Lost connection with the printer");
+ case "maintenance":
+ return catalog.i18nc("@label:status", "Unavailable");
default:
- return catalog.i18nc("@label Printer status", "Unknown");
+ return catalog.i18nc("@label:status", "Unknown");
}
}
@@ -78,7 +78,7 @@ Rectangle
Rectangle
{
- width: Math.floor(parent.width / 3)
+ width: Math.round(parent.width / 3)
height: parent.height
Label // Print job name
@@ -123,7 +123,7 @@ Rectangle
Rectangle
{
- width: Math.floor(parent.width / 3 * 2)
+ width: Math.round(parent.width / 3 * 2)
height: parent.height
Label // Friendly machine name
@@ -131,7 +131,7 @@ Rectangle
id: printerNameLabel
anchors.top: parent.top
anchors.left: parent.left
- width: Math.floor(parent.width / 2 - UM.Theme.getSize("default_margin").width - showCameraIcon.width)
+ width: Math.round(parent.width / 2 - UM.Theme.getSize("default_margin").width - showCameraIcon.width)
text: printer.name
font: UM.Theme.getFont("default_bold")
elide: Text.ElideRight
@@ -141,7 +141,7 @@ Rectangle
{
id: printerTypeLabel
anchors.top: printerNameLabel.bottom
- width: Math.floor(parent.width / 2 - UM.Theme.getSize("default_margin").width)
+ width: Math.round(parent.width / 2 - UM.Theme.getSize("default_margin").width)
text: printer.type
anchors.left: parent.left
elide: Text.ElideRight
@@ -175,7 +175,7 @@ Rectangle
id: extruderInfo
anchors.bottom: parent.bottom
- width: Math.floor(parent.width / 2 - UM.Theme.getSize("default_margin").width)
+ width: Math.round(parent.width / 2 - UM.Theme.getSize("default_margin").width)
height: childrenRect.height
spacing: UM.Theme.getSize("default_margin").width
@@ -183,7 +183,7 @@ Rectangle
PrintCoreConfiguration
{
id: leftExtruderInfo
- width: Math.floor((parent.width - extruderSeperator.width) / 2)
+ width: Math.round((parent.width - extruderSeperator.width) / 2)
printCoreConfiguration: printer.extruders[0]
}
@@ -198,7 +198,7 @@ Rectangle
PrintCoreConfiguration
{
id: rightExtruderInfo
- width: Math.floor((parent.width - extruderSeperator.width) / 2)
+ width: Math.round((parent.width - extruderSeperator.width) / 2)
printCoreConfiguration: printer.extruders[1]
}
}
@@ -209,7 +209,7 @@ Rectangle
anchors.right: parent.right
anchors.top: parent.top
height: showExtended ? parent.height: printProgressTitleBar.height
- width: Math.floor(parent.width / 2 - UM.Theme.getSize("default_margin").width)
+ width: Math.round(parent.width / 2 - UM.Theme.getSize("default_margin").width)
border.width: UM.Theme.getSize("default_lining").width
border.color: lineColor
radius: cornerRadius
@@ -264,6 +264,7 @@ Rectangle
case "wait_for_configuration":
return catalog.i18nc("@label:status", "Reserved")
case "wait_cleanup":
+ case "wait_user_action":
return catalog.i18nc("@label:status", "Finished")
case "pre_print":
case "sent_to_printer":
@@ -278,6 +279,7 @@ Rectangle
case "aborted":
return catalog.i18nc("@label:status", "Print aborted");
default:
+ // If print job has unknown status show printer.status
return printerStatusText(printer);
}
}
diff --git a/plugins/UM3NetworkPrinting/PrinterVideoStream.qml b/plugins/UM3NetworkPrinting/PrinterVideoStream.qml
index 3e6f6a8fd8..7f7b2ad546 100644
--- a/plugins/UM3NetworkPrinting/PrinterVideoStream.qml
+++ b/plugins/UM3NetworkPrinting/PrinterVideoStream.qml
@@ -57,7 +57,7 @@ Item
{
id: cameraImage
width: Math.min(sourceSize.width === 0 ? 800 * screenScaleFactor : sourceSize.width, maximumWidth)
- height: Math.floor((sourceSize.height === 0 ? 600 * screenScaleFactor : sourceSize.height) * width / sourceSize.width)
+ height: Math.round((sourceSize.height === 0 ? 600 * screenScaleFactor : sourceSize.height) * width / sourceSize.width)
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
z: 1
diff --git a/plugins/UM3NetworkPrinting/UM3InfoComponents.qml b/plugins/UM3NetworkPrinting/UM3InfoComponents.qml
index 18b481a6ed..5a9cc096e7 100644
--- a/plugins/UM3NetworkPrinting/UM3InfoComponents.qml
+++ b/plugins/UM3NetworkPrinting/UM3InfoComponents.qml
@@ -10,7 +10,8 @@ Item
{
id: base
- property bool isUM3: Cura.MachineManager.activeQualityDefinitionId == "ultimaker3"
+ property string activeQualityDefinitionId: Cura.MachineManager.activeQualityDefinitionId
+ property bool isUM3: activeQualityDefinitionId == "ultimaker3" || activeQualityDefinitionId.match("ultimaker_") != null
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands
property bool authenticationRequested: printerConnected && (Cura.MachineManager.printerOutputDevices[0].authenticationState == 2 || Cura.MachineManager.printerOutputDevices[0].authenticationState == 5) // AuthState.AuthenticationRequested or AuthenticationReceived.
diff --git a/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py
index 5ff5eb9e3e..089b9038f7 100644
--- a/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py
+++ b/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py
@@ -82,6 +82,9 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
self._zero_conf_browser.cancel()
self._zero_conf_browser = None # Force the old ServiceBrowser to be destroyed.
+ for instance_name in list(self._discovered_devices):
+ self._onRemoveDevice(instance_name)
+
self._zero_conf = Zeroconf()
self._zero_conf_browser = ServiceBrowser(self._zero_conf, u'_ultimaker._tcp.local.',
[self._appendServiceChangedRequest])
diff --git a/plugins/USBPrinting/AutoDetectBaudJob.py b/plugins/USBPrinting/AutoDetectBaudJob.py
index 72f4f20262..50bb831ba8 100644
--- a/plugins/USBPrinting/AutoDetectBaudJob.py
+++ b/plugins/USBPrinting/AutoDetectBaudJob.py
@@ -22,6 +22,7 @@ class AutoDetectBaudJob(Job):
def run(self):
Logger.log("d", "Auto detect baud rate started.")
timeout = 3
+ tries = 2
programmer = Stk500v2()
serial = None
@@ -31,36 +32,38 @@ class AutoDetectBaudJob(Job):
except:
programmer.close()
- for baud_rate in self._all_baud_rates:
- Logger.log("d", "Checking {serial} if baud rate {baud_rate} works".format(serial= self._serial_port, baud_rate = baud_rate))
+ for retry in range(tries):
+ for baud_rate in self._all_baud_rates:
+ Logger.log("d", "Checking {serial} if baud rate {baud_rate} works".format(serial= self._serial_port, baud_rate = baud_rate))
- if serial is None:
- try:
- serial = Serial(str(self._serial_port), baud_rate, timeout = timeout, writeTimeout = timeout)
- except SerialException as e:
- Logger.logException("w", "Unable to create serial")
- continue
- else:
- # We already have a serial connection, just change the baud rate.
- try:
- serial.baudrate = baud_rate
- except:
- continue
- sleep(1.5) # Ensure that we are not talking to the boot loader. 1.5 seconds seems to be the magic number
- successful_responses = 0
-
- serial.write(b"\n") # Ensure we clear out previous responses
- serial.write(b"M105\n")
-
- timeout_time = time() + timeout
-
- while timeout_time > time():
- line = serial.readline()
- if b"ok T:" in line:
- successful_responses += 1
- if successful_responses >= 3:
- self.setResult(baud_rate)
- return
+ if serial is None:
+ try:
+ serial = Serial(str(self._serial_port), baud_rate, timeout = timeout, writeTimeout = timeout)
+ except SerialException as e:
+ Logger.logException("w", "Unable to create serial")
+ continue
+ else:
+ # We already have a serial connection, just change the baud rate.
+ try:
+ serial.baudrate = baud_rate
+ except:
+ continue
+ sleep(1.5) # Ensure that we are not talking to the boot loader. 1.5 seconds seems to be the magic number
+ successful_responses = 0
+ serial.write(b"\n") # Ensure we clear out previous responses
serial.write(b"M105\n")
+
+ timeout_time = time() + timeout
+
+ while timeout_time > time():
+ line = serial.readline()
+ if b"ok T:" in line:
+ successful_responses += 1
+ if successful_responses >= 3:
+ self.setResult(baud_rate)
+ return
+
+ serial.write(b"M105\n")
+ sleep(15) # Give the printer some time to init and try again.
self.setResult(None) # Unable to detect the correct baudrate.
diff --git a/plugins/USBPrinting/USBPrinterOutputController.py b/plugins/USBPrinting/USBPrinterOutputController.py
deleted file mode 100644
index f189ed5876..0000000000
--- a/plugins/USBPrinting/USBPrinterOutputController.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# Copyright (c) 2017 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
-from PyQt5.QtCore import QTimer
-
-MYPY = False
-if MYPY:
- from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
- from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
-
-
-class USBPrinterOutputController(PrinterOutputController):
- def __init__(self, output_device):
- super().__init__(output_device)
-
- self._preheat_bed_timer = QTimer()
- self._preheat_bed_timer.setSingleShot(True)
- self._preheat_bed_timer.timeout.connect(self._onPreheatBedTimerFinished)
- self._preheat_printer = None
-
- def moveHead(self, printer: "PrinterOutputModel", x, y, z, speed):
- self._output_device.sendCommand("G91")
- self._output_device.sendCommand("G0 X%s Y%s Z%s F%s" % (x, y, z, speed))
- self._output_device.sendCommand("G90")
-
- def homeHead(self, printer):
- self._output_device.sendCommand("G28 X")
- self._output_device.sendCommand("G28 Y")
-
- def homeBed(self, printer):
- self._output_device.sendCommand("G28 Z")
-
- def setJobState(self, job: "PrintJobOutputModel", state: str):
- if state == "pause":
- self._output_device.pausePrint()
- job.updateState("paused")
- elif state == "print":
- self._output_device.resumePrint()
- job.updateState("printing")
- elif state == "abort":
- self._output_device.cancelPrint()
- pass
-
- def preheatBed(self, printer: "PrinterOutputModel", temperature, duration):
- try:
- temperature = round(temperature) # The API doesn't allow floating point.
- duration = round(duration)
- except ValueError:
- return # Got invalid values, can't pre-heat.
-
- self.setTargetBedTemperature(printer, temperature=temperature)
- self._preheat_bed_timer.setInterval(duration * 1000)
- self._preheat_bed_timer.start()
- self._preheat_printer = printer
- printer.updateIsPreheating(True)
-
- def cancelPreheatBed(self, printer: "PrinterOutputModel"):
- self.preheatBed(printer, temperature=0, duration=0)
- self._preheat_bed_timer.stop()
- printer.updateIsPreheating(False)
-
- def setTargetBedTemperature(self, printer: "PrinterOutputModel", temperature: int):
- self._output_device.sendCommand("M140 S%s" % temperature)
-
- def _onPreheatBedTimerFinished(self):
- self.setTargetBedTemperature(self._preheat_printer, 0)
- self._preheat_printer.updateIsPreheating(False)
\ No newline at end of file
diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py
index b53f502d81..24feedd628 100644
--- a/plugins/USBPrinting/USBPrinterOutputDevice.py
+++ b/plugins/USBPrinting/USBPrinterOutputDevice.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2016 Ultimaker B.V.
+# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM.Logger import Logger
@@ -10,14 +10,14 @@ from UM.PluginRegistry import PluginRegistry
from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
+from cura.PrinterOutput.GenericOutputController import GenericOutputController
from .AutoDetectBaudJob import AutoDetectBaudJob
-from .USBPrinterOutputController import USBPrinterOutputController
from .avr_isp import stk500v2, intelHex
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty
-from serial import Serial, SerialException
+from serial import Serial, SerialException, SerialTimeoutException
from threading import Thread
from time import time, sleep
from queue import Queue
@@ -99,7 +99,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
Application.getInstance().getController().setActiveStage("MonitorStage")
# find the G-code for the active build plate to print
- active_build_plate_id = Application.getInstance().getBuildPlateModel().activeBuildPlate
+ active_build_plate_id = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
gcode_dict = getattr(Application.getInstance().getController().getScene(), "gcode_dict")
gcode_list = gcode_dict[active_build_plate_id]
@@ -116,7 +116,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
@pyqtSlot(str)
def updateFirmware(self, file):
- self._firmware_location = file
+ # the file path is qurl encoded.
+ self._firmware_location = file.replace("file://", "")
self.showFirmwareInterface()
self.setFirmwareUpdateState(FirmwareUpdateState.updating)
self._update_firmware_thread.start()
@@ -126,9 +127,11 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
if self._connection_state != ConnectionState.closed:
self.close()
- hex_file = intelHex.readHex(self._firmware_location)
- if len(hex_file) == 0:
- Logger.log("e", "Unable to read provided hex file. Could not update firmware")
+ try:
+ hex_file = intelHex.readHex(self._firmware_location)
+ assert len(hex_file) > 0
+ except (FileNotFoundError, AssertionError):
+ Logger.log("e", "Unable to read provided hex file. Could not update firmware.")
self.setFirmwareUpdateState(FirmwareUpdateState.firmware_not_found_error)
return
@@ -198,7 +201,6 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
# Reset line number. If this is not done, first line is sometimes ignored
self._gcode.insert(0, "M110")
self._gcode_position = 0
- self._is_printing = True
self._print_start_time = time()
self._print_estimated_time = int(Application.getInstance().getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.Seconds))
@@ -206,6 +208,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
for i in range(0, 4): # Push first 4 entries before accepting other inputs
self._sendNextGcodeLine()
+ self._is_printing = True
self.writeFinished.emit(self)
def _autoDetectFinished(self, job: AutoDetectBaudJob):
@@ -237,7 +240,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
container_stack = Application.getInstance().getGlobalContainerStack()
num_extruders = container_stack.getProperty("machine_extruder_count", "value")
# Ensure that a printer is created.
- self._printers = [PrinterOutputModel(output_controller=USBPrinterOutputController(self), number_of_extruders=num_extruders)]
+ self._printers = [PrinterOutputModel(output_controller=GenericOutputController(self), number_of_extruders=num_extruders)]
self._printers[0].updateName(container_stack.getName())
self.setConnectionState(ConnectionState.connected)
self._update_thread.start()
@@ -266,8 +269,10 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
command = (command + "\n").encode()
if not command.endswith(b"\n"):
command += b"\n"
- self._serial.write(b"\n")
- self._serial.write(command)
+ try:
+ self._serial.write(command)
+ except SerialTimeoutException:
+ Logger.log("w", "Timeout when sending command to printer via USB.")
def _update(self):
while self._connection_state == ConnectionState.connected and self._serial is not None:
@@ -281,7 +286,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self.sendCommand("M105")
self._last_temperature_request = time()
- if b"ok T:" in line or line.startswith(b"T:"): # Temperature message
+ if b"ok T:" in line or line.startswith(b"T:") or b"ok B:" in line or line.startswith(b"B:"): # Temperature message. 'T:' for extruder and 'B:' for bed
extruder_temperature_matches = re.findall(b"T(\d*): ?([\d\.]+) ?\/?([\d\.]+)?", line)
# Update all temperature values
for match, extruder in zip(extruder_temperature_matches, self._printers[0].extruders):
@@ -299,6 +304,9 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._printers[0].updateTargetBedTemperature(float(match[1]))
if self._is_printing:
+ if line.startswith(b'!!'):
+ Logger.log('e', "Printer signals fatal error. Cancelling print. {}".format(line))
+ self.cancelPrint()
if b"ok" in line:
if not self._command_queue.empty():
self._sendCommand(self._command_queue.get())
@@ -364,7 +372,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
elapsed_time = int(time() - self._print_start_time)
print_job = self._printers[0].activePrintJob
if print_job is None:
- print_job = PrintJobOutputModel(output_controller = USBPrinterOutputController(self), name= Application.getInstance().getPrintInformation().jobName)
+ print_job = PrintJobOutputModel(output_controller = GenericOutputController(self), name= Application.getInstance().getPrintInformation().jobName)
print_job.updateState("printing")
self._printers[0].updateActivePrintJob(print_job)
diff --git a/plugins/UltimakerMachineActions/BedLevelMachineAction.py b/plugins/UltimakerMachineActions/BedLevelMachineAction.py
index 04b6cf1acc..6a8a337d8c 100644
--- a/plugins/UltimakerMachineActions/BedLevelMachineAction.py
+++ b/plugins/UltimakerMachineActions/BedLevelMachineAction.py
@@ -1,3 +1,8 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from typing import List
+
from cura.MachineAction import MachineAction
from cura.PrinterOutputDevice import PrinterOutputDevice
@@ -5,6 +10,7 @@ from UM.FlameProfiler import pyqtSlot
from UM.Application import Application
from UM.i18n import i18nCatalog
+from UM.Logger import Logger
catalog = i18nCatalog("cura")
@@ -26,38 +32,45 @@ class BedLevelMachineAction(MachineAction):
@pyqtSlot()
def startBedLeveling(self):
self._bed_level_position = 0
- printer_output_devices = self._getPrinterOutputDevices()
- if printer_output_devices:
- printer_output_devices[0].homeBed()
- printer_output_devices[0].moveHead(0, 0, 3)
- printer_output_devices[0].homeHead()
- def _getPrinterOutputDevices(self):
+ printer_output_devices = self._getPrinterOutputDevices()
+ if not printer_output_devices:
+ Logger.log("e", "Can't start bed levelling. The printer connection seems to have been lost.")
+ return
+ printer = printer_output_devices[0].activePrinter
+
+ printer.homeBed()
+ printer.moveHead(0, 0, 3)
+ printer.homeHead()
+
+ def _getPrinterOutputDevices(self) -> List[PrinterOutputDevice]:
return [printer_output_device for printer_output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices() if isinstance(printer_output_device, PrinterOutputDevice)]
@pyqtSlot()
def moveToNextLevelPosition(self):
output_devices = self._getPrinterOutputDevices()
- if output_devices: # We found at least one output device
- output_device = output_devices[0]
+ if not output_devices: #No output devices. Can't move.
+ Logger.log("e", "Can't move to the next position. The printer connection seems to have been lost.")
+ return
+ printer = output_devices[0].activePrinter
- if self._bed_level_position == 0:
- output_device.moveHead(0, 0, 3)
- output_device.homeHead()
- output_device.moveHead(0, 0, 3)
- output_device.moveHead(Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value") - 10, 0, 0)
- output_device.moveHead(0, 0, -3)
- self._bed_level_position += 1
- elif self._bed_level_position == 1:
- output_device.moveHead(0, 0, 3)
- output_device.moveHead(-Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value" ) / 2, Application.getInstance().getGlobalContainerStack().getProperty("machine_depth", "value") - 10, 0)
- output_device.moveHead(0, 0, -3)
- self._bed_level_position += 1
- elif self._bed_level_position == 2:
- output_device.moveHead(0, 0, 3)
- output_device.moveHead(-Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value") / 2 + 10, -(Application.getInstance().getGlobalContainerStack().getProperty("machine_depth", "value") + 10), 0)
- output_device.moveHead(0, 0, -3)
- self._bed_level_position += 1
- elif self._bed_level_position >= 3:
- output_device.sendCommand("M18") # Turn off all motors so the user can move the axes
- self.setFinished()
\ No newline at end of file
+ if self._bed_level_position == 0:
+ printer.moveHead(0, 0, 3)
+ printer.homeHead()
+ printer.moveHead(0, 0, 3)
+ printer.moveHead(Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value") - 10, 0, 0)
+ printer.moveHead(0, 0, -3)
+ self._bed_level_position += 1
+ elif self._bed_level_position == 1:
+ printer.moveHead(0, 0, 3)
+ printer.moveHead(-Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value" ) / 2, Application.getInstance().getGlobalContainerStack().getProperty("machine_depth", "value") - 10, 0)
+ printer.moveHead(0, 0, -3)
+ self._bed_level_position += 1
+ elif self._bed_level_position == 2:
+ printer.moveHead(0, 0, 3)
+ printer.moveHead(-Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value") / 2 + 10, -(Application.getInstance().getGlobalContainerStack().getProperty("machine_depth", "value") + 10), 0)
+ printer.moveHead(0, 0, -3)
+ self._bed_level_position += 1
+ elif self._bed_level_position >= 3:
+ output_devices[0].sendCommand("M18") # Turn off all motors so the user can move the axes
+ self.setFinished()
\ No newline at end of file
diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml
index 5a1f8f26a7..b92638aa12 100644
--- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml
+++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml
@@ -180,7 +180,7 @@ Cura.MachineAction
height: childrenRect.height
anchors.top: nozzleTempLabel.top
anchors.left: bedTempStatus.right
- anchors.leftMargin: UM.Theme.getSize("default_margin").width/2
+ anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").width/2)
visible: checkupMachineAction.usbConnected
Button
{
@@ -241,7 +241,7 @@ Cura.MachineAction
height: childrenRect.height
anchors.top: bedTempLabel.top
anchors.left: bedTempStatus.right
- anchors.leftMargin: UM.Theme.getSize("default_margin").width/2
+ anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").width/2)
visible: checkupMachineAction.usbConnected && manager.hasHeatedBed
Button
{
diff --git a/plugins/UserAgreementPlugin/UserAgreement.qml b/plugins/UserAgreementPlugin/UserAgreement.qml
index c7f3f165e3..4ee03f4ad5 100644
--- a/plugins/UserAgreementPlugin/UserAgreement.qml
+++ b/plugins/UserAgreementPlugin/UserAgreement.qml
@@ -9,8 +9,8 @@ import UM 1.3 as UM
UM.Dialog
{
id: baseDialog
- minimumWidth: Math.floor(UM.Theme.getSize("modal_window_minimum").width * 0.75)
- minimumHeight: Math.floor(UM.Theme.getSize("modal_window_minimum").height * 0.5)
+ minimumWidth: Math.round(UM.Theme.getSize("modal_window_minimum").width * 0.75)
+ minimumHeight: Math.round(UM.Theme.getSize("modal_window_minimum").height * 0.5)
width: minimumWidth
height: minimumHeight
title: catalog.i18nc("@title:window", "User Agreement")
diff --git a/plugins/VersionUpgrade/VersionUpgrade26to27/VersionUpgrade26to27.py b/plugins/VersionUpgrade/VersionUpgrade26to27/VersionUpgrade26to27.py
index a2d78d8d9f..2037a0211d 100644
--- a/plugins/VersionUpgrade/VersionUpgrade26to27/VersionUpgrade26to27.py
+++ b/plugins/VersionUpgrade/VersionUpgrade26to27/VersionUpgrade26to27.py
@@ -153,6 +153,10 @@ class VersionUpgrade26to27(VersionUpgrade):
if new_id is not None:
parser.set("containers", key, new_id)
+ if "6" not in parser["containers"]:
+ parser["containers"]["6"] = parser["containers"]["5"]
+ parser["containers"]["5"] = "empty"
+
for each_section in ("general", "metadata"):
if not parser.has_section(each_section):
parser.add_section(each_section)
diff --git a/plugins/VersionUpgrade/VersionUpgrade30to31/VersionUpgrade30to31.py b/plugins/VersionUpgrade/VersionUpgrade30to31/VersionUpgrade30to31.py
index d7b2c1a001..a88ff5ac1c 100644
--- a/plugins/VersionUpgrade/VersionUpgrade30to31/VersionUpgrade30to31.py
+++ b/plugins/VersionUpgrade/VersionUpgrade30to31/VersionUpgrade30to31.py
@@ -3,14 +3,9 @@
import configparser #To parse preference files.
import io #To serialise the preference files afterwards.
-import os
-from urllib.parse import quote_plus
-from UM.Resources import Resources
from UM.VersionUpgrade import VersionUpgrade #We're inheriting from this.
-from cura.CuraApplication import CuraApplication
-
# a list of all legacy "Not Supported" quality profiles
_OLD_NOT_SUPPORTED_PROFILES = [
@@ -130,7 +125,6 @@ class VersionUpgrade30to31(VersionUpgrade):
parser.write(output)
return [filename], [output.getvalue()]
-
## Upgrades a container stack from version 3.0 to 3.1.
#
# \param serialised The serialised form of a container stack.
@@ -172,71 +166,3 @@ class VersionUpgrade30to31(VersionUpgrade):
output = io.StringIO()
parser.write(output)
return [filename], [output.getvalue()]
-
- def _getSingleExtrusionMachineQualityChanges(self, quality_changes_container):
- quality_changes_dir = Resources.getPath(CuraApplication.ResourceTypes.QualityInstanceContainer)
- quality_changes_containers = []
-
- for item in os.listdir(quality_changes_dir):
- file_path = os.path.join(quality_changes_dir, item)
- if not os.path.isfile(file_path):
- continue
-
- parser = configparser.ConfigParser(interpolation = None)
- try:
- parser.read([file_path])
- except:
- # skip, it is not a valid stack file
- continue
-
- if not parser.has_option("metadata", "type"):
- continue
- if "quality_changes" != parser["metadata"]["type"]:
- continue
-
- if not parser.has_option("general", "name"):
- continue
- if quality_changes_container["general"]["name"] != parser["general"]["name"]:
- continue
-
- quality_changes_containers.append(parser)
-
- return quality_changes_containers
-
- def _createExtruderQualityChangesForSingleExtrusionMachine(self, filename, global_quality_changes):
- suffix = "_" + quote_plus(global_quality_changes["general"]["name"].lower())
- machine_name = os.path.os.path.basename(filename).replace(".inst.cfg", "").replace(suffix, "")
-
- # Why is this here?!
- # When we load a .curaprofile file the deserialize will trigger a version upgrade, creating a dangling file.
- # This file can be recognized by it's lack of a machine name in the target filename.
- # So when we detect that situation here, we don't create the file and return.
- if machine_name == "":
- return
-
- new_filename = machine_name + "_" + "fdmextruder" + suffix
-
- extruder_quality_changes_parser = configparser.ConfigParser(interpolation = None)
- extruder_quality_changes_parser.add_section("general")
- extruder_quality_changes_parser["general"]["version"] = str(2)
- extruder_quality_changes_parser["general"]["name"] = global_quality_changes["general"]["name"]
- extruder_quality_changes_parser["general"]["definition"] = global_quality_changes["general"]["definition"]
-
- # check renamed definition
- if extruder_quality_changes_parser["general"]["definition"] in _RENAMED_DEFINITION_DICT:
- extruder_quality_changes_parser["general"]["definition"] = _RENAMED_DEFINITION_DICT[extruder_quality_changes_parser["general"]["definition"]]
-
- extruder_quality_changes_parser.add_section("metadata")
- extruder_quality_changes_parser["metadata"]["quality_type"] = global_quality_changes["metadata"]["quality_type"]
- extruder_quality_changes_parser["metadata"]["type"] = global_quality_changes["metadata"]["type"]
- extruder_quality_changes_parser["metadata"]["setting_version"] = str(4)
- extruder_quality_changes_parser["metadata"]["extruder"] = "fdmextruder"
-
- extruder_quality_changes_output = io.StringIO()
- extruder_quality_changes_parser.write(extruder_quality_changes_output)
- extruder_quality_changes_filename = quote_plus(new_filename) + ".inst.cfg"
-
- quality_changes_dir = Resources.getPath(CuraApplication.ResourceTypes.QualityInstanceContainer)
-
- with open(os.path.join(quality_changes_dir, extruder_quality_changes_filename), "w", encoding = "utf-8") as f:
- f.write(extruder_quality_changes_output.getvalue())
diff --git a/plugins/VersionUpgrade/VersionUpgrade30to31/__init__.py b/plugins/VersionUpgrade/VersionUpgrade30to31/__init__.py
index b4b75dddf7..c853e2b93b 100644
--- a/plugins/VersionUpgrade/VersionUpgrade30to31/__init__.py
+++ b/plugins/VersionUpgrade/VersionUpgrade30to31/__init__.py
@@ -33,6 +33,10 @@ def getMetaData():
"get_version": upgrade.getCfgVersion,
"location": {"./extruders"}
},
+ "quality": {
+ "get_version": upgrade.getCfgVersion,
+ "location": {"./quality"}
+ },
"quality_changes": {
"get_version": upgrade.getCfgVersion,
"location": {"./quality"}
diff --git a/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py b/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py
new file mode 100644
index 0000000000..e39266884d
--- /dev/null
+++ b/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py
@@ -0,0 +1,138 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+import configparser #To parse preference files.
+import io #To serialise the preference files afterwards.
+
+from UM.VersionUpgrade import VersionUpgrade #We're inheriting from this.
+
+## Mapping extruder definition IDs to the positions that they are in.
+_EXTRUDER_TO_POSITION = {
+ "builder_premium_large_front": 1,
+ "builder_premium_large_rear": 0,
+ "builder_premium_medium_front": 1,
+ "builder_premium_medium_rear": 0,
+ "builder_premium_small_front": 1,
+ "builder_premium_small_rear": 0,
+ "cartesio_extruder_0": 0,
+ "cartesio_extruder_1": 1,
+ "cartesio_extruder_2": 2,
+ "cartesio_extruder_3": 3,
+ "custom_extruder_1": 0, #Warning, non-programmers are attempting to count here.
+ "custom_extruder_2": 1,
+ "custom_extruder_3": 2,
+ "custom_extruder_4": 3,
+ "custom_extruder_5": 4,
+ "custom_extruder_6": 5,
+ "custom_extruder_7": 6,
+ "custom_extruder_8": 7,
+ "hBp_extruder_left": 0,
+ "hBp_extruder_right": 1,
+ "makeit_dual_1st": 0,
+ "makeit_dual_2nd": 1,
+ "makeit_l_dual_1st": 0,
+ "makeit_l_dual_2nd": 1,
+ "ord_extruder_0": 0,
+ "ord_extruder_1": 1,
+ "ord_extruder_2": 2,
+ "ord_extruder_3": 3,
+ "ord_extruder_4": 4,
+ "punchtec_connect_xl_extruder_left": 0,
+ "punchtec_connect_xl_extruder_right": 1,
+ "raise3D_N2_dual_extruder_0": 0,
+ "raise3D_N2_dual_extruder_1": 1,
+ "raise3D_N2_plus_dual_extruder_0": 0,
+ "raise3D_N2_plus_dual_extruder_1": 1,
+ "ultimaker3_extended_extruder_left": 0,
+ "ultimaker3_extended_extruder_right": 1,
+ "ultimaker3_extruder_left": 0,
+ "ultimaker3_extruder_right": 1,
+ "ultimaker_original_dual_1st": 0,
+ "ultimaker_original_dual_2nd": 1,
+ "vertex_k8400_dual_1st": 0,
+ "vertex_k8400_dual_2nd": 1
+}
+
+## Upgrades configurations from the state they were in at version 3.2 to the
+# state they should be in at version 3.3.
+class VersionUpgrade32to33(VersionUpgrade):
+
+ temporary_group_name_counter = 1
+ ## Gets the version number from a CFG file in Uranium's 3.2 format.
+ #
+ # Since the format may change, this is implemented for the 3.2 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 a container stack from version 3.2 to 3.3.
+ #
+ # \param serialised The serialised form of a container stack.
+ # \param filename The name of the file to upgrade.
+ def upgradeStack(self, serialized, filename):
+ parser = configparser.ConfigParser(interpolation = None)
+ parser.read_string(serialized)
+
+ if "metadata" in parser and "um_network_key" in parser["metadata"]:
+ if "hidden" not in parser["metadata"]:
+ parser["metadata"]["hidden"] = "False"
+ if "connect_group_name" not in parser["metadata"]:
+ parser["metadata"]["connect_group_name"] = "Temporary group name #" + str(self.temporary_group_name_counter)
+ self.temporary_group_name_counter += 1
+
+ #Update version number.
+ parser["general"]["version"] = "4"
+
+ result = io.StringIO()
+ parser.write(result)
+ return [filename], [result.getvalue()]
+
+ ## Upgrades non-quality-changes 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"] = "3"
+
+ result = io.StringIO()
+ parser.write(result)
+ return [filename], [result.getvalue()]
+
+ ## Upgrades a quality changes container to the new format.
+ def upgradeQualityChanges(self, serialized, filename):
+ parser = configparser.ConfigParser(interpolation = None)
+ parser.read_string(serialized)
+
+ #Extruder quality changes profiles have the extruder position instead of the ID of the extruder definition.
+ if "metadata" in parser and "extruder" in parser["metadata"]: #Only do this for extruder profiles.
+ extruder_id = parser["metadata"]["extruder"]
+ if extruder_id in _EXTRUDER_TO_POSITION:
+ extruder_position = _EXTRUDER_TO_POSITION[extruder_id]
+ else:
+ extruder_position = 0 #The user was using custom extruder definitions. He's on his own then.
+
+ parser["metadata"]["position"] = str(extruder_position)
+ del parser["metadata"]["extruder"]
+
+ quality_type = parser["metadata"]["quality_type"]
+ parser["metadata"]["quality_type"] = quality_type.lower()
+
+ #Update version number.
+ parser["general"]["version"] = "3"
+
+ result = io.StringIO()
+ parser.write(result)
+ return [filename], [result.getvalue()]
\ No newline at end of file
diff --git a/plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py b/plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py
new file mode 100644
index 0000000000..72ff6e1de9
--- /dev/null
+++ b/plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py
@@ -0,0 +1,44 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from . import VersionUpgrade32to33
+
+upgrade = VersionUpgrade32to33.VersionUpgrade32to33()
+
+def getMetaData():
+ return {
+ "version_upgrade": {
+ # From To Upgrade function
+ ("machine_stack", 3000004): ("machine_stack", 4000004, upgrade.upgradeStack),
+ ("extruder_train", 3000004): ("extruder_train", 4000004, upgrade.upgradeStack),
+
+ ("definition_changes", 2000004): ("definition_changes", 3000004, upgrade.upgradeInstanceContainer),
+ ("quality_changes", 2000004): ("quality_changes", 3000004, upgrade.upgradeQualityChanges),
+ ("user", 2000004): ("user", 3000004, upgrade.upgradeInstanceContainer)
+ },
+ "sources": {
+ "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"}
+ },
+ "user": {
+ "get_version": upgrade.getCfgVersion,
+ "location": {"./user"}
+ }
+ }
+ }
+
+def register(app):
+ return { "version_upgrade": upgrade }
\ No newline at end of file
diff --git a/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json b/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json
new file mode 100644
index 0000000000..fbce09c807
--- /dev/null
+++ b/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json
@@ -0,0 +1,8 @@
+ {
+ "name": "Version Upgrade 3.2 to 3.3",
+ "author": "Ultimaker B.V.",
+ "version": "1.0.0",
+ "description": "Upgrades configurations from Cura 3.2 to Cura 3.3.",
+ "api": 4,
+ "i18n-catalog": "cura"
+}
diff --git a/plugins/XRayView/XRayPass.py b/plugins/XRayView/XRayPass.py
index 38c88a256e..a75d393b35 100644
--- a/plugins/XRayView/XRayPass.py
+++ b/plugins/XRayView/XRayPass.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015 Ultimaker B.V.
+# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os.path
@@ -10,7 +10,7 @@ from UM.View.RenderPass import RenderPass
from UM.View.RenderBatch import RenderBatch
from UM.View.GL.OpenGL import OpenGL
-from UM.Scene.SceneNode import SceneNode
+from cura.Scene.CuraSceneNode import CuraSceneNode
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
class XRayPass(RenderPass):
@@ -27,7 +27,7 @@ class XRayPass(RenderPass):
batch = RenderBatch(self._shader, type = RenderBatch.RenderType.NoType, backface_cull = False, blend_mode = RenderBatch.BlendMode.Additive)
for node in DepthFirstIterator(self._scene.getRoot()):
- if type(node) is SceneNode and node.getMeshData() and node.isVisible():
+ if isinstance(node, CuraSceneNode) and node.getMeshData() and node.isVisible():
batch.addItem(node.getWorldTransformation(), node.getMeshData())
self.bind()
diff --git a/plugins/XRayView/xray_composite.shader b/plugins/XRayView/xray_composite.shader
index 82dca52cf9..0a8f6364d7 100644
--- a/plugins/XRayView/xray_composite.shader
+++ b/plugins/XRayView/xray_composite.shader
@@ -13,9 +13,9 @@ vertex =
}
fragment =
- uniform sampler2D u_layer0;
- uniform sampler2D u_layer1;
- uniform sampler2D u_layer2;
+ uniform sampler2D u_layer0; //Default pass.
+ uniform sampler2D u_layer1; //Selection pass.
+ uniform sampler2D u_layer2; //X-ray pass.
uniform vec2 u_offset[9];
@@ -83,9 +83,9 @@ vertex41core =
fragment41core =
#version 410
- uniform sampler2D u_layer0;
- uniform sampler2D u_layer1;
- uniform sampler2D u_layer2;
+ uniform sampler2D u_layer0; //Default pass.
+ uniform sampler2D u_layer1; //Selection pass.
+ uniform sampler2D u_layer2; //X-ray pass.
uniform vec2 u_offset[9];
diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py
index 39cbe5669c..341f2bd3bb 100644
--- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py
+++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py
@@ -48,18 +48,35 @@ class XmlMaterialProfile(InstanceContainer):
## Overridden from InstanceContainer
# set the meta data for all machine / variant combinations
- def setMetaDataEntry(self, key, value):
+ #
+ # The "apply_to_all" flag indicates whether this piece of metadata should be applied to all material containers
+ # or just this specific container.
+ # For example, when you change the material name, you want to apply it to all its derived containers, but for
+ # some specific settings, they should only be applied to a machine/variant-specific container.
+ #
+ def setMetaDataEntry(self, key, value, apply_to_all = True):
registry = ContainerRegistry.getInstance()
if registry.isReadOnly(self.getId()):
return
- super().setMetaDataEntry(key, value)
+ # Prevent recursion
+ if not apply_to_all:
+ super().setMetaDataEntry(key, value)
+ return
- basefile = self.getMetaDataEntry("base_file", self.getId()) #if basefile is self.getId, this is a basefile.
- # Update all containers that share basefile
- for container in registry.findInstanceContainers(base_file = basefile):
- if container.getMetaDataEntry(key, None) != value: # Prevent recursion
- container.setMetaDataEntry(key, value)
+ # Get the MaterialGroup
+ material_manager = CuraApplication.getInstance().getMaterialManager()
+ root_material_id = self.getMetaDataEntry("base_file") #if basefile is self.getId, this is a basefile.
+ material_group = material_manager.getMaterialGroup(root_material_id)
+
+ # Update the root material container
+ root_material_container = material_group.root_material_node.getContainer()
+ root_material_container.setMetaDataEntry(key, value, apply_to_all = False)
+
+ # Update all containers derived from it
+ for node in material_group.derived_material_node_list:
+ container = node.getContainer()
+ container.setMetaDataEntry(key, value, apply_to_all = False)
## Overridden from InstanceContainer, similar to setMetaDataEntry.
# without this function the setName would only set the name of the specific nozzle / material / machine combination container
@@ -183,28 +200,34 @@ class XmlMaterialProfile(InstanceContainer):
## Begin Settings Block
builder.start("settings")
- if self.getDefinition().getId() == "fdmprinter":
+ if self.getMetaDataEntry("definition") == "fdmprinter":
for instance in self.findInstances():
self._addSettingElement(builder, instance)
machine_container_map = {}
- machine_nozzle_map = {}
+ machine_variant_map = {}
+
+ variant_manager = CuraApplication.getInstance().getVariantManager()
+
+ root_material_id = self.getMetaDataEntry("base_file") # if basefile is self.getId, this is a basefile.
+ all_containers = registry.findInstanceContainers(base_file = root_material_id)
- all_containers = registry.findInstanceContainers(GUID = self.getMetaDataEntry("GUID"), base_file = self.getId())
for container in all_containers:
- definition_id = container.getDefinition().getId()
+ definition_id = container.getMetaDataEntry("definition")
if definition_id == "fdmprinter":
continue
if definition_id not in machine_container_map:
machine_container_map[definition_id] = container
- if definition_id not in machine_nozzle_map:
- machine_nozzle_map[definition_id] = {}
+ if definition_id not in machine_variant_map:
+ machine_variant_map[definition_id] = {}
- variant = container.getMetaDataEntry("variant")
- if variant:
- machine_nozzle_map[definition_id][variant] = container
+ variant_name = container.getMetaDataEntry("variant_name")
+ if variant_name:
+ variant_dict = {"variant_node": variant_manager.getVariantNode(definition_id, variant_name),
+ "material_container": container}
+ machine_variant_map[definition_id][variant_name] = variant_dict
continue
machine_container_map[definition_id] = container
@@ -213,7 +236,8 @@ class XmlMaterialProfile(InstanceContainer):
product_id_map = self.getProductIdMap()
for definition_id, container in machine_container_map.items():
- definition = container.getDefinition()
+ definition_id = container.getMetaDataEntry("definition")
+ definition_metadata = registry.findDefinitionContainersMetadata(id = definition_id)[0]
product = definition_id
for product_name, product_id_list in product_id_map.items():
@@ -223,45 +247,74 @@ class XmlMaterialProfile(InstanceContainer):
builder.start("machine")
builder.start("machine_identifier", {
- "manufacturer": container.getMetaDataEntry("machine_manufacturer", definition.getMetaDataEntry("manufacturer", "Unknown")),
+ "manufacturer": container.getMetaDataEntry("machine_manufacturer",
+ definition_metadata.get("manufacturer", "Unknown")),
"product": product
})
builder.end("machine_identifier")
for instance in container.findInstances():
- if self.getDefinition().getId() == "fdmprinter" and self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value:
+ if self.getMetaDataEntry("definition") == "fdmprinter" and self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value:
# If the settings match that of the base profile, just skip since we inherit the base profile.
continue
self._addSettingElement(builder, instance)
# Find all hotend sub-profiles corresponding to this material and machine and add them to this profile.
- for hotend_id, hotend in machine_nozzle_map[definition_id].items():
- variant_containers = registry.findInstanceContainersMetadata(id = hotend.getMetaDataEntry("variant"))
- if not variant_containers:
- continue
+ buildplate_dict = {}
+ for variant_name, variant_dict in machine_variant_map[definition_id].items():
+ variant_type = variant_dict["variant_node"].metadata["hardware_type"]
+ from cura.Machines.VariantManager import VariantType
+ variant_type = VariantType(variant_type)
+ if variant_type == VariantType.NOZZLE:
+ # The hotend identifier is not the containers name, but its "name".
+ builder.start("hotend", {"id": variant_name})
- # The hotend identifier is not the containers name, but its "name".
- builder.start("hotend", {"id": variant_containers[0]["name"]})
+ # Compatible is a special case, as it's added as a meta data entry (instead of an instance).
+ material_container = variant_dict["material_container"]
+ compatible = material_container.getMetaDataEntry("compatible")
+ if compatible is not None:
+ builder.start("setting", {"key": "hardware compatible"})
+ if compatible:
+ builder.data("yes")
+ else:
+ builder.data("no")
+ builder.end("setting")
- # Compatible is a special case, as it's added as a meta data entry (instead of an instance).
- compatible = hotend.getMetaDataEntry("compatible")
- if compatible is not None:
- builder.start("setting", {"key": "hardware compatible"})
- if compatible:
- builder.data("yes")
- else:
- builder.data("no")
- builder.end("setting")
+ for instance in material_container.findInstances():
+ if container.getInstance(instance.definition.key) and container.getProperty(instance.definition.key, "value") == instance.value:
+ # If the settings match that of the machine profile, just skip since we inherit the machine profile.
+ continue
- for instance in hotend.findInstances():
- if container.getInstance(instance.definition.key) and container.getProperty(instance.definition.key, "value") == instance.value:
- # If the settings match that of the machine profile, just skip since we inherit the machine profile.
- continue
+ self._addSettingElement(builder, instance)
- self._addSettingElement(builder, instance)
+ if material_container.getMetaDataEntry("buildplate_compatible") and not buildplate_dict:
+ buildplate_dict["buildplate_compatible"] = material_container.getMetaDataEntry("buildplate_compatible")
+ buildplate_dict["buildplate_recommended"] = material_container.getMetaDataEntry("buildplate_recommended")
+ buildplate_dict["material_container"] = material_container
- builder.end("hotend")
+ builder.end("hotend")
+
+ if buildplate_dict:
+ for variant_name in buildplate_dict["buildplate_compatible"]:
+ builder.start("buildplate", {"id": variant_name})
+
+ material_container = buildplate_dict["material_container"]
+ buildplate_compatible_dict = material_container.getMetaDataEntry("buildplate_compatible")
+ buildplate_recommended_dict = material_container.getMetaDataEntry("buildplate_recommended")
+ if buildplate_compatible_dict:
+ compatible = buildplate_compatible_dict[variant_name]
+ recommended = buildplate_recommended_dict[variant_name]
+
+ builder.start("setting", {"key": "hardware compatible"})
+ builder.data("yes" if compatible else "no")
+ builder.end("setting")
+
+ builder.start("setting", {"key": "hardware recommended"})
+ builder.data("yes" if recommended else "no")
+ builder.end("setting")
+
+ builder.end("buildplate")
builder.end("machine")
@@ -544,7 +597,6 @@ class XmlMaterialProfile(InstanceContainer):
for machine_id in machine_id_list:
definitions = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = machine_id)
if not definitions:
- Logger.log("w", "No definition found for machine ID %s", machine_id)
continue
definition = definitions[0]
@@ -591,14 +643,11 @@ class XmlMaterialProfile(InstanceContainer):
if buildplate_id is None:
continue
- variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(
- id = buildplate_id)
- if not variant_containers:
- # It is not really properly defined what "ID" is so also search for variants by name.
- variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(
- definition = machine_id, name = buildplate_id)
-
- if not variant_containers:
+ from cura.Machines.VariantManager import VariantType
+ variant_manager = CuraApplication.getInstance().getVariantManager()
+ variant_node = variant_manager.getVariantNode(machine_id, buildplate_id,
+ variant_type = VariantType.BUILD_PLATE)
+ if not variant_node:
continue
buildplate_compatibility = machine_compatibility
@@ -619,16 +668,14 @@ class XmlMaterialProfile(InstanceContainer):
hotends = machine.iterfind("./um:hotend", self.__namespaces)
for hotend in hotends:
- hotend_id = hotend.get("id")
- if hotend_id is None:
+ # The "id" field for hotends in material profiles are actually
+ hotend_name = hotend.get("id")
+ if hotend_name is None:
continue
- variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = hotend_id)
- if not variant_containers:
- # It is not really properly defined what "ID" is so also search for variants by name.
- variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = machine_id, name = hotend_id)
-
- if not variant_containers:
+ variant_manager = CuraApplication.getInstance().getVariantManager()
+ variant_node = variant_manager.getVariantNode(machine_id, hotend_name)
+ if not variant_node:
continue
hotend_compatibility = machine_compatibility
@@ -644,20 +691,20 @@ class XmlMaterialProfile(InstanceContainer):
else:
Logger.log("d", "Unsupported material setting %s", key)
- new_hotend_id = self.getId() + "_" + machine_id + "_" + hotend_id.replace(" ", "_")
+ new_hotend_specific_material_id = self.getId() + "_" + machine_id + "_" + hotend_name.replace(" ", "_")
# Same as machine compatibility, keep the derived material containers consistent with the parent material
- if ContainerRegistry.getInstance().isLoaded(new_hotend_id):
- new_hotend_material = ContainerRegistry.getInstance().findContainers(id = new_hotend_id)[0]
+ if ContainerRegistry.getInstance().isLoaded(new_hotend_specific_material_id):
+ new_hotend_material = ContainerRegistry.getInstance().findContainers(id = new_hotend_specific_material_id)[0]
is_new_material = False
else:
- new_hotend_material = XmlMaterialProfile(new_hotend_id)
+ new_hotend_material = XmlMaterialProfile(new_hotend_specific_material_id)
is_new_material = True
new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData()))
- new_hotend_material.getMetaData()["id"] = new_hotend_id
+ new_hotend_material.getMetaData()["id"] = new_hotend_specific_material_id
new_hotend_material.getMetaData()["name"] = self.getName()
- new_hotend_material.getMetaData()["variant"] = variant_containers[0]["id"]
+ new_hotend_material.getMetaData()["variant_name"] = hotend_name
new_hotend_material.setDefinition(machine_id)
# Don't use setMetadata, as that overrides it for all materials with same base file
new_hotend_material.getMetaData()["compatible"] = hotend_compatibility
@@ -777,7 +824,6 @@ class XmlMaterialProfile(InstanceContainer):
for machine_id in machine_id_list:
definition_metadata = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = machine_id)
if not definition_metadata:
- Logger.log("w", "No definition found for machine ID %s", machine_id)
continue
definition_metadata = definition_metadata[0]
@@ -787,15 +833,11 @@ class XmlMaterialProfile(InstanceContainer):
if machine_compatibility:
new_material_id = container_id + "_" + machine_id
- # The child or derived material container may already exist. This can happen when a material in a
- # project file and the a material in Cura have the same ID.
- # In the case if a derived material already exists, override that material container because if
- # the data in the parent material has been changed, the derived ones should be updated too.
- found_materials = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = new_material_id)
- if found_materials:
- new_material_metadata = found_materials[0]
- else:
- new_material_metadata = {}
+ # Do not look for existing container/container metadata with the same ID although they may exist.
+ # In project loading and perhaps some other places, we only want to get information (metadata)
+ # from a file without changing the current state of the system. If we overwrite the existing
+ # metadata here, deserializeMetadata() will not be safe for retrieving information.
+ new_material_metadata = {}
new_material_metadata.update(base_metadata)
new_material_metadata["id"] = new_material_id
@@ -803,8 +845,7 @@ class XmlMaterialProfile(InstanceContainer):
new_material_metadata["machine_manufacturer"] = machine_manufacturer
new_material_metadata["definition"] = machine_id
- if len(found_materials) == 0: #This is a new material.
- result_metadata.append(new_material_metadata)
+ result_metadata.append(new_material_metadata)
buildplates = machine.iterfind("./um:buildplate", cls.__namespaces)
buildplate_map = {}
@@ -815,15 +856,17 @@ class XmlMaterialProfile(InstanceContainer):
if buildplate_id is None:
continue
- variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = buildplate_id)
- if not variant_containers:
+ variant_metadata = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = buildplate_id)
+ if not variant_metadata:
# It is not really properly defined what "ID" is so also search for variants by name.
- variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = machine_id, name = buildplate_id)
+ variant_metadata = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = machine_id, name = buildplate_id)
- if not variant_containers:
+ if not variant_metadata:
continue
settings = buildplate.iterfind("./um:setting", cls.__namespaces)
+ buildplate_compatibility = True
+ buildplate_recommended = True
for entry in settings:
key = entry.get("key")
if key == "hardware compatible":
@@ -831,50 +874,36 @@ class XmlMaterialProfile(InstanceContainer):
elif key == "hardware recommended":
buildplate_recommended = cls._parseCompatibleValue(entry.text)
- buildplate_map["buildplate_compatible"][buildplate_id] = buildplate_map["buildplate_compatible"]
- buildplate_map["buildplate_recommended"][buildplate_id] = buildplate_map["buildplate_recommended"]
+ buildplate_map["buildplate_compatible"][buildplate_id] = buildplate_compatibility
+ buildplate_map["buildplate_recommended"][buildplate_id] = buildplate_recommended
for hotend in machine.iterfind("./um:hotend", cls.__namespaces):
- hotend_id = hotend.get("id")
- if hotend_id is None:
+ hotend_name = hotend.get("id")
+ if hotend_name is None:
continue
- variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = hotend_id)
- if not variant_containers:
- # It is not really properly defined what "ID" is so also search for variants by name.
- variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = machine_id, name = hotend_id)
-
hotend_compatibility = machine_compatibility
for entry in hotend.iterfind("./um:setting", cls.__namespaces):
key = entry.get("key")
if key == "hardware compatible":
hotend_compatibility = cls._parseCompatibleValue(entry.text)
- new_hotend_id = container_id + "_" + machine_id + "_" + hotend_id.replace(" ", "_")
+ new_hotend_specific_material_id = container_id + "_" + machine_id + "_" + hotend_name.replace(" ", "_")
- # Same as machine compatibility, keep the derived material containers consistent with the parent material
- found_materials = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = new_hotend_id)
- if found_materials:
- new_hotend_material_metadata = found_materials[0]
- else:
- new_hotend_material_metadata = {}
+ # Same as above, do not overwrite existing metadata.
+ new_hotend_material_metadata = {}
new_hotend_material_metadata.update(base_metadata)
- if variant_containers:
- new_hotend_material_metadata["variant"] = variant_containers[0]["id"]
- else:
- new_hotend_material_metadata["variant"] = hotend_id
- _with_missing_variants.append(new_hotend_material_metadata)
+ new_hotend_material_metadata["variant_name"] = hotend_name
new_hotend_material_metadata["compatible"] = hotend_compatibility
new_hotend_material_metadata["machine_manufacturer"] = machine_manufacturer
- new_hotend_material_metadata["id"] = new_hotend_id
+ new_hotend_material_metadata["id"] = new_hotend_specific_material_id
new_hotend_material_metadata["definition"] = machine_id
if buildplate_map["buildplate_compatible"]:
new_hotend_material_metadata["buildplate_compatible"] = buildplate_map["buildplate_compatible"]
new_hotend_material_metadata["buildplate_recommended"] = buildplate_map["buildplate_recommended"]
- if len(found_materials) == 0:
- result_metadata.append(new_hotend_material_metadata)
+ result_metadata.append(new_hotend_material_metadata)
# there is only one ID for a machine. Once we have reached here, it means we have already found
# a workable ID for that machine, so there is no need to continue
@@ -916,11 +945,11 @@ class XmlMaterialProfile(InstanceContainer):
else:
merged_name_parts.append(part)
- id_list = [name.lower().replace(" ", ""), # simply removing all spaces
+ id_list = {name.lower().replace(" ", ""), # simply removing all spaces
name.lower().replace(" ", "_"), # simply replacing all spaces with underscores
"_".join(merged_name_parts),
- ]
-
+ }
+ id_list = list(id_list)
return id_list
## Gets a mapping from product names in the XML files to their definition
@@ -954,7 +983,8 @@ class XmlMaterialProfile(InstanceContainer):
"retraction amount": "retraction_amount",
"retraction speed": "retraction_speed",
"adhesion tendency": "material_adhesion_tendency",
- "surface energy": "material_surface_energy"
+ "surface energy": "material_surface_energy",
+ "shrinkage percentage": "material_shrinkage_percentage",
}
__unmapped_settings = [
"hardware compatible",
@@ -994,21 +1024,3 @@ def _indent(elem, level = 0):
# before the last }
def _tag_without_namespace(element):
return element.tag[element.tag.rfind("}") + 1:]
-
-#While loading XML profiles, some of these profiles don't know what variant
-#they belong to. We'd like to search by the machine ID and the variant's
-#name, but we don't know the variant's ID. Not all variants have been loaded
-#yet so we can't run a filter on the name and machine. The ID is unknown
-#so we can't lazily load the variant either. So we have to wait until all
-#the rest is loaded properly and then assign the correct variant to the
-#material files that were missing it.
-_with_missing_variants = []
-def _fillMissingVariants():
- registry = ContainerRegistry.getInstance()
- for variant_metadata in _with_missing_variants:
- variants = registry.findContainersMetadata(definition = variant_metadata["definition"], name = variant_metadata["variant"])
- if not variants:
- Logger.log("w", "Could not find variant for variant-specific material {material_id}.".format(material_id = variant_metadata["id"]))
- continue
- variant_metadata["variant"] = variants[0]["id"]
-ContainerRegistry.allMetadataLoaded.connect(_fillMissingVariants)
diff --git a/resources/definitions/anycubic_i3_mega.def.json b/resources/definitions/anycubic_i3_mega.def.json
index cba868900c..7106038193 100644
--- a/resources/definitions/anycubic_i3_mega.def.json
+++ b/resources/definitions/anycubic_i3_mega.def.json
@@ -12,7 +12,7 @@
"platform": "anycubic_i3_mega_platform.stl",
"has_materials": false,
"has_machine_quality": true,
- "preferred_quality": "*normal*"
+ "preferred_quality_type": "normal"
},
"overrides":
diff --git a/resources/definitions/bq_hephestos_xl.def.json b/resources/definitions/bq_hephestos_xl.def.json
index 75b756c71e..08be4b8d34 100644
--- a/resources/definitions/bq_hephestos_xl.def.json
+++ b/resources/definitions/bq_hephestos_xl.def.json
@@ -6,7 +6,7 @@
"visible": true,
"manufacturer": "BQ",
"author": "BQ",
- "file_formats": "text/x-code",
+ "file_formats": "text/x-gcode",
"platform": "bq_hephestos_platform.stl",
"platform_offset": [ 0, -82, 0]
},
diff --git a/resources/definitions/builder_premium_large.def.json b/resources/definitions/builder_premium_large.def.json
index b496dc524e..deb1539a9a 100644
--- a/resources/definitions/builder_premium_large.def.json
+++ b/resources/definitions/builder_premium_large.def.json
@@ -12,16 +12,14 @@
"platform": "builder_premium_platform.stl",
"platform_offset": [-126, -36, 117],
"has_machine_quality": true,
- "preferred_quality": "*Normal*",
+ "preferred_quality_type": "normal",
"machine_extruder_trains":
{
"0": "builder_premium_large_rear",
"1": "builder_premium_large_front"
}
},
-
-
-
+
"overrides": {
"machine_name": { "default_value": "Builder Premium Large" },
"machine_heated_bed": { "default_value": true },
@@ -36,7 +34,7 @@
"default_material_print_temperature": { "value": "215" },
"material_print_temperature_layer_0": { "value": "material_print_temperature + 5" },
"material_standby_temperature": { "value": "material_print_temperature" },
-
+
"switch_extruder_retraction_speeds": {"default_value": 15 },
"switch_extruder_retraction_speed": {"default_value": 15 },
"switch_extruder_prime_speed": {"default_value": 15 },
@@ -58,9 +56,9 @@
"prime_tower_wipe_enabled": { "default_value": false },
"prime_tower_min_volume": { "default_value": 50 },
"dual_pre_wipe": { "default_value": false },
-
+
"prime_blob_enable": { "enabled": true },
-
+
"acceleration_enabled": { "value": "True" },
"acceleration_layer_0": { "value": "acceleration_topbottom" },
"acceleration_prime_tower": { "value": "math.ceil(acceleration_print * 2000 / 4000)" },
@@ -71,7 +69,7 @@
"acceleration_travel": { "value": "acceleration_print" },
"acceleration_wall": { "value": "math.ceil(acceleration_print * 1000 / 3000)" },
"acceleration_wall_0": { "value": "math.ceil(acceleration_wall * 1000 / 1000)" },
-
+
"cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" },
"cool_min_layer_time": { "default_value": 10 },
@@ -84,9 +82,9 @@
"jerk_topbottom": { "value": "math.ceil(jerk_print * 5 / 25)" },
"jerk_wall": { "value": "math.ceil(jerk_print * 10 / 25)" },
"jerk_wall_0": { "value": "math.ceil(jerk_wall * 5 / 10)" },
-
+
"wall_thickness": { "value": "1.2" },
-
+
"retraction_amount": { "default_value": 3 },
"retraction_speed": { "default_value": 15 },
"retraction_retract_speed": { "default_value": 15 },
@@ -113,4 +111,4 @@
},
"machine_extruder_count": { "default_value": 2 }
}
-}
\ No newline at end of file
+}
diff --git a/resources/definitions/builder_premium_medium.def.json b/resources/definitions/builder_premium_medium.def.json
index fe8a039fc4..c28c7c5de6 100644
--- a/resources/definitions/builder_premium_medium.def.json
+++ b/resources/definitions/builder_premium_medium.def.json
@@ -12,16 +12,14 @@
"platform": "builder_premium_platform.stl",
"platform_offset": [-126, -36, 117],
"has_machine_quality": true,
- "preferred_quality": "*Normal*",
+ "preferred_quality_type": "normal",
"machine_extruder_trains":
{
"0": "builder_premium_medium_rear",
"1": "builder_premium_medium_front"
}
},
-
-
-
+
"overrides": {
"machine_name": { "default_value": "Builder Premium Medium" },
"machine_heated_bed": { "default_value": true },
@@ -36,7 +34,7 @@
"default_material_print_temperature": { "value": "215" },
"material_print_temperature_layer_0": { "value": "material_print_temperature + 5" },
"material_standby_temperature": { "value": "material_print_temperature" },
-
+
"switch_extruder_retraction_speeds": {"default_value": 15 },
"switch_extruder_retraction_speed": {"default_value": 15 },
"switch_extruder_prime_speed": {"default_value": 15 },
@@ -58,9 +56,9 @@
"prime_tower_wipe_enabled": { "default_value": false },
"prime_tower_min_volume": { "default_value": 50 },
"dual_pre_wipe": { "default_value": false },
-
+
"prime_blob_enable": { "enabled": true },
-
+
"acceleration_enabled": { "value": "True" },
"acceleration_layer_0": { "value": "acceleration_topbottom" },
"acceleration_prime_tower": { "value": "math.ceil(acceleration_print * 2000 / 4000)" },
@@ -71,7 +69,7 @@
"acceleration_travel": { "value": "acceleration_print" },
"acceleration_wall": { "value": "math.ceil(acceleration_print * 1000 / 3000)" },
"acceleration_wall_0": { "value": "math.ceil(acceleration_wall * 1000 / 1000)" },
-
+
"cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" },
"cool_min_layer_time": { "default_value": 10 },
@@ -84,9 +82,9 @@
"jerk_topbottom": { "value": "math.ceil(jerk_print * 5 / 25)" },
"jerk_wall": { "value": "math.ceil(jerk_print * 10 / 25)" },
"jerk_wall_0": { "value": "math.ceil(jerk_wall * 5 / 10)" },
-
+
"wall_thickness": { "value": "1.2" },
-
+
"retraction_amount": { "default_value": 3 },
"retraction_speed": { "default_value": 15 },
"retraction_retract_speed": { "default_value": 15 },
@@ -113,4 +111,4 @@
},
"machine_extruder_count": { "default_value": 2 }
}
-}
\ No newline at end of file
+}
diff --git a/resources/definitions/builder_premium_small.def.json b/resources/definitions/builder_premium_small.def.json
index a1660b63cf..8e2fe44631 100644
--- a/resources/definitions/builder_premium_small.def.json
+++ b/resources/definitions/builder_premium_small.def.json
@@ -11,16 +11,14 @@
"platform": "builder_premium_platform.stl",
"platform_offset": [-126, -36, 117],
"has_machine_quality": true,
- "preferred_quality": "*Normal*",
+ "preferred_quality_type": "normal",
"machine_extruder_trains":
{
"0": "builder_premium_small_rear",
"1": "builder_premium_small_front"
}
},
-
-
-
+
"overrides": {
"machine_name": { "default_value": "Builder Premium Small" },
"machine_heated_bed": { "default_value": true },
@@ -35,7 +33,7 @@
"default_material_print_temperature": { "value": "215" },
"material_print_temperature_layer_0": { "value": "material_print_temperature + 5" },
"material_standby_temperature": { "value": "material_print_temperature" },
-
+
"switch_extruder_retraction_speeds": {"default_value": 15 },
"switch_extruder_retraction_speed": {"default_value": 15 },
"switch_extruder_prime_speed": {"default_value": 15 },
@@ -57,9 +55,9 @@
"prime_tower_wipe_enabled": { "default_value": false },
"prime_tower_min_volume": { "default_value": 50 },
"dual_pre_wipe": { "default_value": false },
-
+
"prime_blob_enable": { "enabled": true },
-
+
"acceleration_enabled": { "value": "True" },
"acceleration_layer_0": { "value": "acceleration_topbottom" },
"acceleration_prime_tower": { "value": "math.ceil(acceleration_print * 2000 / 4000)" },
@@ -70,7 +68,7 @@
"acceleration_travel": { "value": "acceleration_print" },
"acceleration_wall": { "value": "math.ceil(acceleration_print * 1000 / 3000)" },
"acceleration_wall_0": { "value": "math.ceil(acceleration_wall * 1000 / 1000)" },
-
+
"cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" },
"cool_min_layer_time": { "default_value": 10 },
@@ -83,9 +81,9 @@
"jerk_topbottom": { "value": "math.ceil(jerk_print * 5 / 25)" },
"jerk_wall": { "value": "math.ceil(jerk_print * 10 / 25)" },
"jerk_wall_0": { "value": "math.ceil(jerk_wall * 5 / 10)" },
-
+
"wall_thickness": { "value": "1.2" },
-
+
"retraction_amount": { "default_value": 3 },
"retraction_speed": { "default_value": 15 },
"retraction_retract_speed": { "default_value": 15 },
@@ -112,4 +110,4 @@
},
"machine_extruder_count": { "default_value": 2 }
}
-}
\ No newline at end of file
+}
diff --git a/resources/definitions/cartesio.def.json b/resources/definitions/cartesio.def.json
index 44f3153015..5ca891d6c5 100644
--- a/resources/definitions/cartesio.def.json
+++ b/resources/definitions/cartesio.def.json
@@ -5,7 +5,7 @@
"metadata": {
"visible": true,
"author": "Scheepers",
- "manufacturer": "Cartesio bv",
+ "manufacturer": "MaukCC",
"file_formats": "text/x-gcode",
"has_machine_quality": true,
@@ -14,10 +14,10 @@
"has_variant_materials": true,
"has_variants": true,
- "variants_name": "Nozzle size",
- "preferred_variant": "*0.8*",
- "preferred_material": "*pla*",
- "preferred_quality": "*normal*",
+ "variants_name": "Tool",
+ "preferred_variant_name": "0.8 mm",
+ "preferred_material": "generic_pla",
+ "preferred_quality_type": "normal",
"machine_extruder_trains":
{
@@ -44,7 +44,7 @@
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"material_print_temp_wait": { "default_value": false },
"material_bed_temp_wait": { "default_value": false },
- "prime_tower_enable": { "default_value": true },
+ "prime_tower_enable": { "default_value": false },
"prime_tower_wall_thickness": { "resolve": 0.7 },
"prime_tower_size": { "value": 24.0 },
"prime_tower_position_x": { "value": 125 },
@@ -55,15 +55,16 @@
[[215, 135], [-215, 135], [-215, 75], [215, 75]]
]},
"machine_start_gcode": {
- "default_value": "\nM92 E159 ;2288 for V5 extruder\n\nM140 S{material_bed_temperature_layer_0}\nM104 S120 T1\nM104 S120 T2\nM104 S120 T3\n\nG21\nG90\nM42 S255 P13 ;chamber lights\nM42 S255 P12 ;fume extraction\nM204 S300 ;default acceleration\nM205 X10 ;default jerk\n\nM117 Homing Y ......\nG28 Y\nM117 Homing X ......\nG28 X\nM117 Homing Z ......\nG28 Z F100\nG1 Z10 F600\nG1 X70 Y20 F9000;go to wipe point\n\nM190 S{material_bed_temperature_layer_0}\n\nM117 Heating for 50 sec.\nG4 S20\nM117 Heating for 30 sec.\nG4 S20\nM117 Heating for 10 sec.\nM300 S1200 P1000\nG4 S9\n\nM117 purging nozzle....\nT0\nG92 E0;set E\nG1 E10 F100\nG92 E0\nG1 E-1 F600\n\nM117 wiping nozzle....\nG1 X1 Y24 F3000\nG1 X70 F9000\nG1 Z10 F900\n\nM104 S21 T1\nM104 S21 T2\nM104 S21 T3\n\nM117 Printing .....\n"
+ "default_value": "\nM92 E159 ;2288 for V5 extruder\n\nM140 S{material_bed_temperature_layer_0}\nM104 T1 S120\nM104 T2 S120\nM104 T3 S120\n\nG21\nG90\nM42 S255 P13 ;chamber lights\nM42 S255 P12 ;fume extraction\nM204 S300 ;default acceleration\nM205 X10 ;default jerk\n\nM117 Homing Y ......\nG28 Y\nM117 Homing X ......\nG28 X\nM117 Homing Z ......\nG28 Z F100\nG1 Z10 F600\nG1 X70 Y20 F9000;go to wipe point\n\nM190 S{material_bed_temperature_layer_0}\n\nM117 Heating for 50 sec.\nG4 S20\nM117 Heating for 30 sec.\nG4 S20\nM117 Heating for 10 sec.\nM300 S1200 P1000\nG4 S9\n\nM117 purging nozzle....\nT0\nG92 E0;set E\nG1 E10 F100\nG92 E0\nG1 E-1 F600\n\nM117 wiping nozzle....\nG1 X1 Y24 F3000\nG1 X70 F9000\nG1 Z10 F900\n\nM104 T1 S21\nM104 T2 S21\nM104 T3 S21\n\nM117 Printing .....\n"
},
"machine_end_gcode": {
- "default_value": "; -- END GCODE --\nM117 cooling down....\nM106 S255\nM140 S5\nM104 S5 T0\nM104 S5 T1\nM104 S5 T2\nM104 S5 T3\n\nG91\nG1 Z1 F900\nG90\n\nG1 X20.0 Y260.0 F6000\nG4 S7\nM84\nG4 S90\nM107\nM42 P12 S0\nM42 P13 S0\nM84\nT0\nM117 Finished.\n; -- end of GCODE --"
+ "default_value": "; -- END GCODE --\nM117 cooling down....\nM106 S255\nM140 S5\nM104 T0 S5\nM104 T1 S5\nM104 T2 S5\nM104 T3 S5\n\nG91\nG1 Z1 F900\nG90\n\nG1 X20.0 Y260.0 F6000\nG4 S7\nM84\nG4 S90\nM107\nM42 P12 S0\nM42 P13 S0\nM84\nT0\nM117 Finished.\n; -- end of GCODE --"
},
"layer_height": { "maximum_value": "(0.8 * min(extruderValues('machine_nozzle_size')))" },
"layer_height_0": { "maximum_value": "(0.8 * min(extruderValues('machine_nozzle_size')))" },
"retraction_extra_prime_amount": { "minimum_value_warning": "-2.0" },
"optimize_wall_printing_order": { "default_value": true },
+ "material_initial_print_temperature": {"maximum_value_warning": "material_print_temperature + 15" },
"machine_nozzle_heat_up_speed": {"default_value": 20},
"machine_nozzle_cool_down_speed": {"default_value": 20},
"machine_min_cool_heat_time_window": {"default_value": 5}
diff --git a/resources/definitions/creality_cr10.def.json b/resources/definitions/creality_cr10.def.json
index 7a58adcd4d..eb0b8c7306 100644
--- a/resources/definitions/creality_cr10.def.json
+++ b/resources/definitions/creality_cr10.def.json
@@ -7,7 +7,7 @@
"author": "Michael Wildermuth",
"manufacturer": "Creality3D",
"file_formats": "text/x-gcode",
- "preferred_quality": "*Draft*"
+ "preferred_quality_type": "draft"
},
"overrides": {
"machine_width": {
diff --git a/resources/definitions/dagoma_discoeasy200.def.json b/resources/definitions/dagoma_discoeasy200.def.json
index 9bcc2402f2..4f0fddc41d 100644
--- a/resources/definitions/dagoma_discoeasy200.def.json
+++ b/resources/definitions/dagoma_discoeasy200.def.json
@@ -7,7 +7,6 @@
"author": "Dagoma",
"manufacturer": "Dagoma",
"file_formats": "text/x-gcode",
- "icon": "icon_discoeasy200.png",
"platform": "discoeasy200.stl",
"platform_offset": [ 105, -59, 280]
},
@@ -39,13 +38,25 @@
"default_value": 10
},
"machine_start_gcode": {
- "default_value": ";Gcode by Cura\nG90 ;absolute positioning\nM106 S250 ;fan on for the palpeur\nG28 X Y\nG1 X50\nM109 S180\nG28\nM104 S{material_print_temperature_layer_0}\n;Activation palpeur\n;bloc palpeur\nG29 ;Auto level\nM107 ;start with the fan off\nG1 X100 Y20 F3000\nG1 Z0.5\nM109 S{material_print_temperature_layer_0}\nM82 ;set extruder to absolute mode\nG92 E0 ;zero the extruded length\nG1 F200 E10 ;extrude 10mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 Z3\nG1 F6000"
+ "default_value": ";Gcode by Cura\nG90\nM106 S250\nG28 X Y\nG1 X50\nM109 S180\nG28\nM104 S{material_print_temperature_layer_0}\nG29\nM107\nG1 X100 Y20 F3000\nG1 Z0.5\nM109 S{material_print_temperature_layer_0}\nM82\nG92 E0\nG1 F200 E10\nG92 E0\nG1 Z3\nG1 F6000\n"
},
"machine_end_gcode": {
- "default_value": "M104 S0\nM106 S255 ;start fan full power\nM140 S0 ;heated bed heater off (if you have it)\n;Home machine\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+3 F3000 ;move Z up a bit and retract filament even more\nG90\nG28 X Y\n;Ventilation forcee\nM107 ;stop fan\n;Shut down motor\nM84 ;shut down motors"
+ "default_value": "\nM104 S0\nM106 S255\nM140 S0\nG91\nG1 E-1 F300\nG1 Z+3 F3000\nG90\nG28 X Y\nM107\nM84\n"
},
"material_diameter": {
"default_value": 1.75
+ },
+ "speed_print": {
+ "default_value": 60
+ },
+ "speed_travel": {
+ "value": "100"
+ },
+ "retraction_amount": {
+ "default_value": 3.5
+ },
+ "retraction_speed": {
+ "default_value": 50
}
}
}
diff --git a/resources/definitions/dagoma_neva.def.json b/resources/definitions/dagoma_neva.def.json
new file mode 100644
index 0000000000..21a557ac22
--- /dev/null
+++ b/resources/definitions/dagoma_neva.def.json
@@ -0,0 +1,69 @@
+{
+ "id": "Dagoma_neva",
+ "name": "Dagoma NEVA",
+ "version": 2,
+ "inherits": "fdmprinter",
+ "metadata": {
+ "visible": true,
+ "author": "Dagoma",
+ "manufacturer": "Dagoma",
+ "file_formats": "text/x-gcode",
+ "platform": "neva.stl",
+ "platform_offset": [ 0, 0, 0]
+ },
+ "overrides": {
+ "machine_width": {
+ "default_value": 195.55
+ },
+ "machine_height": {
+ "default_value": 205
+ },
+ "machine_depth": {
+ "default_value": 195.55
+ },
+ "machine_center_is_zero": {
+ "default_value": true
+ },
+ "machine_nozzle_size": {
+ "default_value": 0.4
+ },
+ "machine_head_with_fans_polygon": {
+ "default_value": [
+ [17, 40],
+ [17, -70],
+ [-17, -70],
+ [17, 40]
+ ]
+ },
+ "gantry_height": {
+ "default_value": 0
+ },
+ "machine_shape": {
+ "default_value": "elliptic"
+ },
+ "machine_gcode_flavor": {
+ "default_value": "RepRap (RepRap)"
+ },
+ "machine_start_gcode": {
+ "default_value": ";Gcode by Cura\nG90\nG28\nM109 S100\nG29\nM104 S{material_print_temperature_layer_0}\nG0 X0 Y-85\nG0 Z0.26\nM109 S{material_print_temperature_layer_0}\nM82\nG92 E0\nG1 F200 E6\nG92 E0\nG1 F200 E-3.5\nG0 Z0.15\nG0 X10\nG0 Z3\nG1 F6000\n"
+ },
+ "machine_end_gcode": {
+ "default_value": "\nM104 S0\nM106 S255\nM140 S0\nG91\nG1 E-1 F300\nG1 Z+3 E-2 F9000\nG90\nG28\n"
+ },
+ "material_diameter": {
+ "default_value": 1.75
+ },
+ "speed_print": {
+ "default_value": 40
+ },
+ "speed_travel": {
+ "default_value": 120
+ },
+ "retraction_amount": {
+ "default_value": 3.8
+ },
+ "retraction_speed": {
+ "default_value": 60
+ }
+ }
+}
diff --git a/resources/definitions/fabtotum.def.json b/resources/definitions/fabtotum.def.json
index 87ce11a35c..d66de07c4a 100644
--- a/resources/definitions/fabtotum.def.json
+++ b/resources/definitions/fabtotum.def.json
@@ -13,18 +13,18 @@
"has_machine_quality": true,
"has_variants": true,
"variants_name": "Head",
- "preferred_variant": "*lite04*",
- "preferred_material": "*fabtotum_pla*",
+ "preferred_variant_name": "Lite 0.4 mm",
+ "preferred_material": "fabtotum_pla",
"supports_usb_connection": false
},
"overrides": {
"machine_name": { "default_value": "FABtotum Personal Fabricator" },
"machine_start_gcode": {
- "default_value": ";Layer height: {layer_height}\n;Walls: {wall_thickness}\n;Fill: {infill_sparse_density}\n;Top\\Bottom Thickness: {top_bottom_thickness}\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nG4 S1 ;1 millisecond pause to buffer the bep bep \nM728 ;FAB bep bep (start the print, go check the oozing and skirt lines adesion) \nG4 S1 ;1 second pause to reach the printer (run fast)\nG92 E0 ;zero the extruded length \nG1 F200 E35 ;slowly extrude 35mm of filament to clean the nozzle and build up extrusion pressure \nG92 E0 ;zero the extruded length again \nG1 F{speed_travel} ;Set travel speed \n;print"
+ "default_value": ";Layer height: {layer_height}\n;Walls: {wall_thickness}\n;Fill: {infill_sparse_density}\n;Top\\Bottom Thickness: {top_bottom_thickness}\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nG4 S1 ;1 millisecond pause to buffer the bep bep \nM300 S2 ;FAB bep bep (start the print, go check the oozing and skirt lines adesion) \nG4 S1 ;1 second pause to reach the printer (run fast)\nG92 E0 ;zero the extruded length \nG1 F200 E35 ;slowly extrude 35mm of filament to clean the nozzle and build up extrusion pressure \nG92 E0 ;zero the extruded length again \n;print"
},
"machine_end_gcode": {
- "default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-3 X+5 Y+5 F5000 ;move Z up a bit and retract filament even more\n;end of the print\nM84 ;steppers off\nG90 ;absolute positioning\nM728 ;FAB bep bep (end print)"
+ "default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-3 X+5 Y+5 F5000 ;move Z up a bit and retract filament even more\n;end of the print\nM84 ;steppers off\nG90 ;absolute positioning\nM300 S2 ;FAB bep bep (end print)"
},
"gantry_height": { "default_value": 55 },
"machine_width": { "default_value": 214 },
diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json
index bdce508df0..d7d9698439 100644
--- a/resources/definitions/fdmprinter.def.json
+++ b/resources/definitions/fdmprinter.def.json
@@ -11,8 +11,8 @@
"file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj;application/x3g",
"visible": false,
"has_materials": true,
- "preferred_material": "*generic_pla*",
- "preferred_quality": "*normal*",
+ "preferred_material": "generic_pla",
+ "preferred_quality_type": "normal",
"machine_extruder_trains":
{
"0": "fdmextruder"
@@ -51,8 +51,8 @@
},
"machine_start_gcode":
{
- "label": "Start GCode",
- "description": "Gcode commands to be executed at the very start - separated by \\n.",
+ "label": "Start G-code",
+ "description": "G-code commands to be executed at the very start - separated by \\n.",
"default_value": "G28 ;Home\nG1 Z15.0 F6000 ;Move the platform down 15mm\n;Prime the extruder\nG92 E0\nG1 F200 E3\nG92 E0",
"type": "str",
"settable_per_mesh": false,
@@ -61,8 +61,8 @@
},
"machine_end_gcode":
{
- "label": "End GCode",
- "description": "Gcode commands to be executed at the very end - separated by \\n.",
+ "label": "End G-code",
+ "description": "G-code commands to be executed at the very end - separated by \\n.",
"default_value": "M104 S0\nM140 S0\n;Retract the filament\nG92 E1\nG1 E-1 F300\nG28 X0 Y0\nM84",
"type": "str",
"settable_per_mesh": false,
@@ -163,7 +163,7 @@
"options":
{
"glass": "Glass",
- "aluminium": "Aluminium"
+ "aluminum": "Aluminum"
},
"settable_per_mesh": false,
"settable_per_extruder": false,
@@ -211,6 +211,18 @@
"settable_per_extruder": false,
"settable_per_meshgroup": false
},
+ "extruders_enabled_count":
+ {
+ "label": "Number of Extruders that are enabled",
+ "description": "Number of extruder trains that are enabled; automatically set in software",
+ "value": "machine_extruder_count",
+ "minimum_value": "1",
+ "maximum_value": "16",
+ "type": "int",
+ "settable_per_mesh": false,
+ "settable_per_extruder": false,
+ "settable_per_meshgroup": false
+ },
"machine_nozzle_tip_outer_diameter":
{
"label": "Outer nozzle diameter",
@@ -312,8 +324,8 @@
},
"machine_gcode_flavor":
{
- "label": "Gcode flavour",
- "description": "The type of gcode to be generated.",
+ "label": "G-code flavour",
+ "description": "The type of g-code to be generated.",
"type": "enum",
"options":
{
@@ -620,6 +632,73 @@
"settable_per_extruder": false,
"settable_per_meshgroup": false
},
+ "machine_steps_per_mm_x":
+ {
+ "label": "Steps per Millimeter (X)",
+ "description": "How many steps of the stepper motor will result in one millimeter of movement in the X direction.",
+ "type": "int",
+ "default_value": 50,
+ "minimum_value": "0.0000001",
+ "settable_per_mesh": false,
+ "settable_per_extruder": true
+ },
+ "machine_steps_per_mm_y":
+ {
+ "label": "Steps per Millimeter (Y)",
+ "description": "How many steps of the stepper motor will result in one millimeter of movement in the Y direction.",
+ "type": "int",
+ "default_value": 50,
+ "minimum_value": "0.0000001",
+ "settable_per_mesh": false,
+ "settable_per_extruder": true
+ },
+ "machine_steps_per_mm_z":
+ {
+ "label": "Steps per Millimeter (Z)",
+ "description": "How many steps of the stepper motor will result in one millimeter of movement in the Z direction.",
+ "type": "int",
+ "default_value": 50,
+ "minimum_value": "0.0000001",
+ "settable_per_mesh": false,
+ "settable_per_extruder": true
+ },
+ "machine_steps_per_mm_e":
+ {
+ "label": "Steps per Millimeter (E)",
+ "description": "How many steps of the stepper motors will result in one millimeter of extrusion.",
+ "type": "int",
+ "default_value": 1600,
+ "minimum_value": "0.0000001",
+ "settable_per_mesh": false,
+ "settable_per_extruder": true
+ },
+ "machine_endstop_positive_direction_x":
+ {
+ "label": "X Endstop in Positive Direction",
+ "description": "Whether the endstop of the X axis is in the positive direction (high X coordinate) or negative (low X coordinate).",
+ "type": "bool",
+ "default_value": false,
+ "settable_per_mesh": false,
+ "settable_per_extruder": true
+ },
+ "machine_endstop_positive_direction_y":
+ {
+ "label": "Y Endstop in Positive Direction",
+ "description": "Whether the endstop of the Y axis is in the positive direction (high Y coordinate) or negative (low Y coordinate).",
+ "type": "bool",
+ "default_value": false,
+ "settable_per_mesh": false,
+ "settable_per_extruder": true
+ },
+ "machine_endstop_positive_direction_z":
+ {
+ "label": "Z Endstop in Positive Direction",
+ "description": "Whether the endstop of the Z axis is in the positive direction (high Z coordinate) or negative (low Z coordinate).",
+ "type": "bool",
+ "default_value": true,
+ "settable_per_mesh": false,
+ "settable_per_extruder": true
+ },
"machine_minimum_feedrate":
{
"label": "Minimum Feedrate",
@@ -630,6 +709,16 @@
"settable_per_mesh": false,
"settable_per_extruder": false,
"settable_per_meshgroup": false
+ },
+ "machine_feeder_wheel_diameter":
+ {
+ "label": "Feeder Wheel Diameter",
+ "description": "The diameter of the wheel that drives the material in the feeder.",
+ "unit": "mm",
+ "type": "float",
+ "default_value": 10.0,
+ "settable_per_mesh": false,
+ "settable_per_extruder": true
}
}
},
@@ -887,7 +976,7 @@
"settable_per_extruder": false,
"settable_per_meshgroup": true,
"settable_globally": true,
- "enabled": "machine_extruder_count > 1",
+ "enabled": "extruders_enabled_count > 1",
"children": {
"wall_0_extruder_nr":
{
@@ -900,7 +989,7 @@
"settable_per_extruder": false,
"settable_per_meshgroup": true,
"settable_globally": true,
- "enabled": "machine_extruder_count > 1"
+ "enabled": "extruders_enabled_count > 1"
},
"wall_x_extruder_nr":
{
@@ -913,7 +1002,7 @@
"settable_per_extruder": false,
"settable_per_meshgroup": true,
"settable_globally": true,
- "enabled": "machine_extruder_count > 1"
+ "enabled": "extruders_enabled_count > 1"
}
}
},
@@ -970,7 +1059,7 @@
"settable_per_extruder": false,
"settable_per_meshgroup": true,
"settable_globally": true,
- "enabled": "machine_extruder_count > 1 and max(extruderValues('roofing_layer_count')) > 0 and max(extruderValues('top_layers')) > 0"
+ "enabled": "extruders_enabled_count > 1 and max(extruderValues('roofing_layer_count')) > 0 and max(extruderValues('top_layers')) > 0"
},
"roofing_layer_count":
{
@@ -995,7 +1084,7 @@
"settable_per_extruder": false,
"settable_per_meshgroup": true,
"settable_globally": true,
- "enabled": "machine_extruder_count > 1"
+ "enabled": "extruders_enabled_count > 1"
},
"top_bottom_thickness":
{
@@ -1465,7 +1554,7 @@
"settable_per_extruder": false,
"settable_per_meshgroup": true,
"settable_globally": true,
- "enabled": "machine_extruder_count > 1"
+ "enabled": "extruders_enabled_count > 1"
},
"infill_sparse_density":
{
@@ -1527,8 +1616,10 @@
"label": "Connect Infill Lines",
"description": "Connect the ends where the infill pattern meets the inner wall using a line which follows the shape of the inner wall. Enabling this setting can make the infill adhere to the walls better and reduce the effects of infill on the quality of vertical surfaces. Disabling this setting reduces the amount of material used.",
"type": "bool",
- "default_value": true,
- "enabled": "infill_pattern == 'cross' or infill_pattern == 'cross_3d'",
+ "default_value": false,
+ "value": "infill_pattern == 'cross' or infill_pattern == 'cross_3d'",
+ "enabled": "infill_pattern == 'grid' or infill_pattern == 'triangles' or infill_pattern == 'trihexagon' or infill_pattern == 'cubic' or infill_pattern == 'tetrahedral' or infill_pattern == 'quarter_cubic' or infill_pattern == 'cross' or infill_pattern == 'cross_3d'",
+ "limit_to_extruder": "infill_extruder_nr",
"settable_per_mesh": true
},
"infill_angles":
@@ -1914,7 +2005,7 @@
"minimum_value": "0",
"maximum_value_warning": "10.0",
"maximum_value": "machine_nozzle_heat_up_speed",
- "enabled": "material_flow_dependent_temperature or (machine_extruder_count > 1 and material_final_print_temperature != material_print_temperature)",
+ "enabled": "material_flow_dependent_temperature or (extruders_enabled_count > 1 and material_final_print_temperature != material_print_temperature)",
"settable_per_mesh": false,
"settable_per_extruder": true
},
@@ -2006,6 +2097,19 @@
"settable_per_mesh": false,
"settable_per_extruder": true
},
+ "material_shrinkage_percentage":
+ {
+ "label": "Shrinkage Ratio",
+ "description": "Shrinkage ratio in percentage.",
+ "unit": "%",
+ "type": "float",
+ "default_value": 0,
+ "minimum_value": "0",
+ "maximum_value": "100",
+ "enabled": false,
+ "settable_per_mesh": false,
+ "settable_per_extruder": true
+ },
"material_flow":
{
"label": "Flow",
@@ -2135,6 +2239,7 @@
"default_value": 1.5,
"value": "line_width * 2",
"minimum_value": "0",
+ "minimum_value_warning": "line_width * 1.5",
"maximum_value_warning": "10",
"enabled": "retraction_enable",
"settable_per_mesh": false,
@@ -2176,7 +2281,7 @@
"minimum_value": "-273.15",
"minimum_value_warning": "0",
"maximum_value_warning": "260",
- "enabled": "machine_extruder_count > 1 and machine_nozzle_temp_enabled",
+ "enabled": "extruders_enabled_count > 1 and machine_nozzle_temp_enabled",
"settable_per_mesh": false,
"settable_per_extruder": true
},
@@ -3278,7 +3383,7 @@
"description": "After the machine switched from one extruder to the other, the build plate is lowered to create clearance between the nozzle and the print. This prevents the nozzle from leaving oozed material on the outside of a print.",
"type": "bool",
"default_value": true,
- "enabled": "retraction_hop_enabled and machine_extruder_count > 1",
+ "enabled": "retraction_hop_enabled and extruders_enabled_count > 1",
"settable_per_mesh": false,
"settable_per_extruder": true
}
@@ -3456,7 +3561,8 @@
"description": "The extruder train to use for printing the support. This is used in multi-extrusion.",
"type": "extruder",
"default_value": "0",
- "enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
+ "value": "-1",
+ "enabled": "(support_enable or support_tree_enable) and extruders_enabled_count > 1",
"settable_per_mesh": false,
"settable_per_extruder": false,
"children": {
@@ -3467,7 +3573,7 @@
"type": "extruder",
"default_value": "0",
"value": "support_extruder_nr",
- "enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
+ "enabled": "(support_enable or support_tree_enable) and extruders_enabled_count > 1",
"settable_per_mesh": false,
"settable_per_extruder": false
},
@@ -3478,7 +3584,7 @@
"type": "extruder",
"default_value": "0",
"value": "support_extruder_nr",
- "enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
+ "enabled": "(support_enable or support_tree_enable) and extruders_enabled_count > 1",
"settable_per_mesh": false,
"settable_per_extruder": false
},
@@ -3489,7 +3595,7 @@
"type": "extruder",
"default_value": "0",
"value": "support_extruder_nr",
- "enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
+ "enabled": "(support_enable or support_tree_enable) and extruders_enabled_count > 1",
"settable_per_mesh": false,
"settable_per_extruder": false,
"children":
@@ -3501,7 +3607,7 @@
"type": "extruder",
"default_value": "0",
"value": "support_interface_extruder_nr",
- "enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
+ "enabled": "(support_enable or support_tree_enable) and extruders_enabled_count > 1",
"settable_per_mesh": false,
"settable_per_extruder": false
},
@@ -3512,7 +3618,7 @@
"type": "extruder",
"default_value": "0",
"value": "support_interface_extruder_nr",
- "enabled": "(support_enable or support_tree_enable) and machine_extruder_count > 1",
+ "enabled": "(support_enable or support_tree_enable) and extruders_enabled_count > 1",
"settable_per_mesh": false,
"settable_per_extruder": false
}
@@ -3571,6 +3677,18 @@
"settable_per_mesh": false,
"settable_per_extruder": true
},
+ "zig_zaggify_support":
+ {
+ "label": "Connect Support Lines",
+ "description": "Connect the ends of the support lines together. Enabling this setting can make your support more sturdy and reduce underextrusion, but it will cost more material.",
+ "type": "bool",
+ "default_value": false,
+ "value": "support_pattern == 'cross'",
+ "enabled": "support_pattern == 'grid' or support_pattern == 'triangles' or support_pattern == 'cross'",
+ "limit_to_extruder": "support_infill_extruder_nr",
+ "settable_per_mesh": false,
+ "settable_per_extruder": true
+ },
"support_connect_zigzags":
{
"label": "Connect Support ZigZags",
@@ -4170,7 +4288,8 @@
"description": "The extruder train to use for printing the skirt/brim/raft. This is used in multi-extrusion.",
"type": "extruder",
"default_value": "0",
- "enabled": "machine_extruder_count > 1 and resolveOrValue('adhesion_type') != 'none'",
+ "value": "-1",
+ "enabled": "extruders_enabled_count > 1 and resolveOrValue('adhesion_type') != 'none'",
"settable_per_mesh": false,
"settable_per_extruder": false
},
@@ -4741,12 +4860,23 @@
"label": "Enable Prime Tower",
"description": "Print a tower next to the print which serves to prime the material after each nozzle switch.",
"type": "bool",
- "enabled": "machine_extruder_count > 1",
+ "enabled": "extruders_enabled_count > 1",
"default_value": false,
"resolve": "any(extruderValues('prime_tower_enable'))",
"settable_per_mesh": false,
"settable_per_extruder": false
},
+ "prime_tower_circular":
+ {
+ "label": "Circular Prime Tower",
+ "description": "Make the prime tower as a circular shape.",
+ "type": "bool",
+ "enabled": "resolveOrValue('prime_tower_enable')",
+ "default_value": true,
+ "resolve": "any(extruderValues('prime_tower_circular'))",
+ "settable_per_mesh": false,
+ "settable_per_extruder": false
+ },
"prime_tower_size":
{
"label": "Prime Tower Size",
@@ -4770,8 +4900,9 @@
"unit": "mm³",
"type": "float",
"default_value": 10,
+ "value": "8.48 if prime_tower_circular else 10",
"minimum_value": "0",
- "maximum_value_warning": "resolveOrValue('prime_tower_size') ** 2 * resolveOrValue('layer_height')",
+ "maximum_value_warning": "round((resolveOrValue('prime_tower_size') * 0.5) ** 2 * 3.14159 * resolveOrValue('layer_height'), 2) if prime_tower_circular else resolveOrValue('prime_tower_size') ** 2 * resolveOrValue('layer_height')",
"enabled": "resolveOrValue('prime_tower_enable')",
"settable_per_mesh": false,
"settable_per_extruder": true,
@@ -4784,7 +4915,7 @@
"unit": "mm",
"type": "float",
"default_value": 2,
- "value": "round(max(2 * prime_tower_line_width, 0.5 * (prime_tower_size - math.sqrt(max(0, prime_tower_size ** 2 - prime_tower_min_volume / layer_height)))), 3)",
+ "value": "round(max(2 * prime_tower_line_width, (0.5 * (prime_tower_size - math.sqrt(max(0, prime_tower_size ** 2 - 4 * prime_tower_min_volume / (3.14159 * layer_height))))) if prime_tower_circular else (0.5 * (prime_tower_size - math.sqrt(max(0, prime_tower_size ** 2 - prime_tower_min_volume / layer_height))))), 3)",
"resolve": "max(extruderValues('prime_tower_wall_thickness'))",
"minimum_value": "0.001",
"minimum_value_warning": "2 * min(extruderValues('prime_tower_line_width')) - 0.0001",
@@ -4877,7 +5008,7 @@
"description": "Enable exterior ooze shield. This will create a shell around the model which is likely to wipe a second nozzle if it's at the same height as the first nozzle.",
"type": "bool",
"resolve": "any(extruderValues('ooze_shield_enabled'))",
- "enabled": "machine_extruder_count > 1",
+ "enabled": "extruders_enabled_count > 1",
"default_value": false,
"settable_per_mesh": false,
"settable_per_extruder": false
@@ -4947,7 +5078,7 @@
"meshfix_keep_open_polygons":
{
"label": "Keep Disconnected Faces",
- "description": "Normally Cura tries to stitch up small holes in the mesh and remove parts of a layer with big holes. Enabling this option keeps those parts which cannot be stitched. This option should be used as a last resort option when everything else fails to produce proper GCode.",
+ "description": "Normally Cura tries to stitch up small holes in the mesh and remove parts of a layer with big holes. Enabling this option keeps those parts which cannot be stitched. This option should be used as a last resort option when everything else fails to produce proper g-code.",
"type": "bool",
"default_value": false,
"settable_per_mesh": true
@@ -4970,7 +5101,7 @@
"description": "Remove areas where multiple meshes are overlapping with each other. This may be used if merged dual material objects overlap with each other.",
"type": "bool",
"default_value": true,
- "value": "machine_extruder_count > 1",
+ "value": "extruders_enabled_count > 1",
"settable_per_mesh": false,
"settable_per_extruder": false,
"settable_per_meshgroup": true
@@ -5017,7 +5148,7 @@
"one_at_a_time": "One at a Time"
},
"default_value": "all_at_once",
- "enabled": "machine_extruder_count == 1",
+ "enabled": "extruders_enabled_count == 1",
"settable_per_mesh": false,
"settable_per_extruder": false,
"settable_per_meshgroup": false
@@ -5162,7 +5293,7 @@
"relative_extrusion":
{
"label": "Relative Extrusion",
- "description": "Use relative extrusion rather than absolute extrusion. Using relative E-steps makes for easier post-processing of the Gcode. However, it's not supported by all printers and it may produce very slight deviations in the amount of deposited material compared to absolute E-steps. Irrespective of this setting, the extrusion mode will always be set to absolute before any Gcode script is output.",
+ "description": "Use relative extrusion rather than absolute extrusion. Using relative E-steps makes for easier post-processing of the g-code. However, it's not supported by all printers and it may produce very slight deviations in the amount of deposited material compared to absolute E-steps. Irrespective of this setting, the extrusion mode will always be set to absolute before any g-code script is output.",
"type": "bool",
"default_value": false,
"value": "machine_gcode_flavor==\"RepRap (RepRap)\"",
@@ -6202,6 +6333,258 @@
"settable_per_mesh": false,
"settable_per_extruder": false,
"settable_per_meshgroup": false
+ },
+ "bridge_settings_enabled":
+ {
+ "label": "Enable Bridge Settings",
+ "description": "Detect bridges and modify print speed, flow and fan settings while bridges are printed.",
+ "type": "bool",
+ "default_value": false,
+ "settable_per_mesh": true,
+ "settable_per_extruder": false,
+ "settable_per_meshgroup": false
+ },
+ "bridge_wall_min_length":
+ {
+ "label": "Minimum Bridge Wall Length",
+ "description": "Unsupported walls shorter than this will be printed using the normal wall settings. Longer unsupported walls will be printed using the bridge wall settings.",
+ "unit": "mm",
+ "type": "float",
+ "minimum_value": "0",
+ "default_value": 5,
+ "enabled": "bridge_settings_enabled",
+ "settable_per_mesh": false,
+ "settable_per_extruder": true
+ },
+ "bridge_skin_support_threshold":
+ {
+ "label": "Bridge Skin Support Threshold",
+ "description": "If a skin region is supported for less than this percentage of its area, print it using the bridge settings. Otherwise it is printed using the normal skin settings.",
+ "unit": "%",
+ "default_value": 50,
+ "type": "float",
+ "minimum_value": "0",
+ "maximum_value": "100",
+ "enabled": "bridge_settings_enabled",
+ "settable_per_mesh": true
+ },
+ "bridge_wall_max_overhang":
+ {
+ "label": "Bridge Wall Max Overhang",
+ "description": "The maximum allowed width of the region of air below a wall line before the wall is printed using bridge settings. Expressed as a percentage of the wall line width. When the air gap is wider than this, the wall line is printed using the bridge settings. Otherwise, the wall line is printed using the normal settings. The lower the value, the more likely it is that overhung wall lines will be printed using bridge settings.",
+ "unit": "%",
+ "default_value": 100,
+ "type": "float",
+ "minimum_value": "0",
+ "maximum_value": "100",
+ "enabled": "bridge_settings_enabled",
+ "settable_per_mesh": true
+ },
+ "bridge_wall_coast":
+ {
+ "label": "Bridge Wall Coasting",
+ "description": "This controls the distance the extruder should coast immediately before a bridge wall begins. Coasting before the bridge starts can reduce the pressure in the nozzle and may produce a flatter bridge.",
+ "unit": "%",
+ "default_value": 100,
+ "type": "float",
+ "minimum_value": "0",
+ "maximum_value": "500",
+ "enabled": "bridge_settings_enabled",
+ "settable_per_mesh": false
+ },
+ "bridge_wall_speed":
+ {
+ "label": "Bridge Wall Speed",
+ "description": "The speed at which the bridge walls are printed.",
+ "unit": "mm/s",
+ "type": "float",
+ "minimum_value": "cool_min_speed",
+ "maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)",
+ "maximum_value_warning": "300",
+ "default_value": 15,
+ "value": "max(cool_min_speed, speed_wall_0 / 2)",
+ "enabled": "bridge_settings_enabled",
+ "settable_per_mesh": true
+ },
+ "bridge_wall_material_flow":
+ {
+ "label": "Bridge Wall Flow",
+ "description": "When printing bridge walls, the amount of material extruded is multiplied by this value.",
+ "unit": "%",
+ "default_value": 50,
+ "type": "float",
+ "minimum_value": "5",
+ "minimum_value_warning": "50",
+ "maximum_value_warning": "150",
+ "enabled": "bridge_settings_enabled",
+ "settable_per_mesh": true
+ },
+ "bridge_skin_speed":
+ {
+ "label": "Bridge Skin Speed",
+ "description": "The speed at which bridge skin regions are printed.",
+ "unit": "mm/s",
+ "type": "float",
+ "minimum_value": "cool_min_speed",
+ "maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)",
+ "maximum_value_warning": "300",
+ "default_value": 15,
+ "value": "max(cool_min_speed, speed_topbottom / 2)",
+ "enabled": "bridge_settings_enabled",
+ "settable_per_mesh": true
+ },
+ "bridge_skin_material_flow":
+ {
+ "label": "Bridge Skin Flow",
+ "description": "When printing bridge skin regions, the amount of material extruded is multiplied by this value.",
+ "unit": "%",
+ "default_value": 60,
+ "type": "float",
+ "minimum_value": "5",
+ "minimum_value_warning": "50",
+ "maximum_value_warning": "150",
+ "enabled": "bridge_settings_enabled",
+ "settable_per_mesh": true
+ },
+ "bridge_skin_density":
+ {
+ "label": "Bridge Skin Density",
+ "description": "The density of the bridge skin layer. Values less than 100 will increase the gaps between the skin lines.",
+ "unit": "%",
+ "default_value": 100,
+ "type": "float",
+ "minimum_value": "5",
+ "maximum_value": "100",
+ "minimum_value_warning": "20",
+ "enabled": "bridge_settings_enabled",
+ "settable_per_mesh": true
+ },
+ "bridge_fan_speed":
+ {
+ "label": "Bridge Fan Speed",
+ "description": "Percentage fan speed to use when printing bridge walls and skin.",
+ "unit": "%",
+ "minimum_value": "0",
+ "maximum_value": "100",
+ "default_value": 100,
+ "type": "float",
+ "enabled": "bridge_settings_enabled",
+ "settable_per_mesh": true
+ },
+ "bridge_enable_more_layers":
+ {
+ "label": "Bridge Has Multiple Layers",
+ "description": "If enabled, the second and third layers above the air are printed using the following settings. Otherwise, those layers are printed using the normal settings.",
+ "type": "bool",
+ "default_value": true,
+ "enabled": "bridge_settings_enabled",
+ "settable_per_mesh": true
+ },
+ "bridge_skin_speed_2":
+ {
+ "label": "Bridge Second Skin Speed",
+ "description": "Print speed to use when printing the second bridge skin layer.",
+ "unit": "mm/s",
+ "type": "float",
+ "minimum_value": "cool_min_speed",
+ "maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)",
+ "maximum_value_warning": "300",
+ "default_value": 25,
+ "value": "bridge_skin_speed",
+ "enabled": "bridge_settings_enabled and bridge_enable_more_layers",
+ "settable_per_mesh": true
+ },
+ "bridge_skin_material_flow_2":
+ {
+ "label": "Bridge Second Skin Flow",
+ "description": "When printing the second bridge skin layer, the amount of material extruded is multiplied by this value.",
+ "unit": "%",
+ "default_value": 100,
+ "type": "float",
+ "minimum_value": "5",
+ "maximum_value": "500",
+ "minimum_value_warning": "50",
+ "maximum_value_warning": "150",
+ "enabled": "bridge_settings_enabled and bridge_enable_more_layers",
+ "settable_per_mesh": true
+ },
+ "bridge_skin_density_2":
+ {
+ "label": "Bridge Second Skin Density",
+ "description": "The density of the second bridge skin layer. Values less than 100 will increase the gaps between the skin lines.",
+ "unit": "%",
+ "default_value": 75,
+ "type": "float",
+ "minimum_value": "5",
+ "maximum_value": "100",
+ "minimum_value_warning": "20",
+ "enabled": "bridge_settings_enabled and bridge_enable_more_layers",
+ "settable_per_mesh": true
+ },
+ "bridge_fan_speed_2":
+ {
+ "label": "Bridge Second Skin Fan Speed",
+ "description": "Percentage fan speed to use when printing the second bridge skin layer.",
+ "unit": "%",
+ "minimum_value": "0",
+ "maximum_value": "100",
+ "default_value": 0,
+ "type": "float",
+ "enabled": "bridge_settings_enabled and bridge_enable_more_layers",
+ "settable_per_mesh": true
+ },
+ "bridge_skin_speed_3":
+ {
+ "label": "Bridge Third Skin Speed",
+ "description": "Print speed to use when printing the third bridge skin layer.",
+ "unit": "mm/s",
+ "type": "float",
+ "minimum_value": "cool_min_speed",
+ "maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)",
+ "maximum_value_warning": "300",
+ "default_value": 15,
+ "value": "bridge_skin_speed",
+ "enabled": "bridge_settings_enabled and bridge_enable_more_layers",
+ "settable_per_mesh": true
+ },
+ "bridge_skin_material_flow_3":
+ {
+ "label": "Bridge Third Skin Flow",
+ "description": "When printing the third bridge skin layer, the amount of material extruded is multiplied by this value.",
+ "unit": "%",
+ "default_value": 110,
+ "type": "float",
+ "minimum_value": "5",
+ "maximum_value": "500",
+ "minimum_value_warning": "50",
+ "maximum_value_warning": "150",
+ "enabled": "bridge_settings_enabled and bridge_enable_more_layers",
+ "settable_per_mesh": true
+ },
+ "bridge_skin_density_3":
+ {
+ "label": "Bridge Third Skin Density",
+ "description": "The density of the third bridge skin layer. Values less than 100 will increase the gaps between the skin lines.",
+ "unit": "%",
+ "default_value": 80,
+ "type": "float",
+ "minimum_value": "5",
+ "maximum_value": "100",
+ "minimum_value_warning": "20",
+ "enabled": "bridge_settings_enabled and bridge_enable_more_layers",
+ "settable_per_mesh": true
+ },
+ "bridge_fan_speed_3":
+ {
+ "label": "Bridge Third Skin Fan Speed",
+ "description": "Percentage fan speed to use when printing the third bridge skin layer.",
+ "unit": "%",
+ "minimum_value": "0",
+ "maximum_value": "100",
+ "default_value": 0,
+ "type": "float",
+ "enabled": "bridge_settings_enabled and bridge_enable_more_layers",
+ "settable_per_mesh": true
}
}
},
diff --git a/resources/definitions/gmax15plus.def.json b/resources/definitions/gmax15plus.def.json
index d651a86bb3..897d492bb2 100644
--- a/resources/definitions/gmax15plus.def.json
+++ b/resources/definitions/gmax15plus.def.json
@@ -10,11 +10,10 @@
"category": "Other",
"file_formats": "text/x-gcode",
"platform": "gmax_1-5_xt-plus_s3d_full model_150707.stl",
+ "has_machine_quality": true,
"has_variants": true,
"variants_name": "Hotend",
- "preferred_variant": "*0.5mm E3D (Default)*"
-
-
+ "preferred_variant_name": "0.5mm E3D (Default)"
},
"overrides": {
diff --git a/resources/definitions/gmax15plus_dual.def.json b/resources/definitions/gmax15plus_dual.def.json
index 4e9a91e88d..8c57c8af63 100644
--- a/resources/definitions/gmax15plus_dual.def.json
+++ b/resources/definitions/gmax15plus_dual.def.json
@@ -11,12 +11,13 @@
"file_formats": "text/x-gcode",
"platform": "gmax_1-5_xt-plus_s3d_full model_150707.stl",
"has_variants": true,
+ "has_machine_quality": true,
"variants_name": "Hotend",
- "preferred_variant": "*0.5mm E3D (Default)*",
+ "preferred_variant_name": "0.5mm E3D (Default)",
"machine_extruder_trains": {
"0": "gmax15plus_dual_extruder_0",
"1": "gmax15plus_dual_extruder_1"
- }
+ }
},
"overrides": {
diff --git a/resources/definitions/imade3d_jellybox.def.json b/resources/definitions/imade3d_jellybox.def.json
index 11df730408..b234e4b2cd 100644
--- a/resources/definitions/imade3d_jellybox.def.json
+++ b/resources/definitions/imade3d_jellybox.def.json
@@ -9,9 +9,8 @@
"platform": "imade3d_jellybox_platform.stl",
"platform_offset": [ 0, -0.3, 0],
"file_formats": "text/x-gcode",
- "preferred_variant": "*0.4*",
- "preferred_material": "*generic_pla*",
- "preferred_quality": "*fast*",
+ "preferred_variant_name": "0.4 mm",
+ "preferred_quality_type": "fast",
"has_materials": true,
"has_variants": true,
"has_machine_materials": true,
diff --git a/resources/definitions/malyan_m180.def.json b/resources/definitions/malyan_m180.def.json
index 5e0a6038dd..11b61328ed 100644
--- a/resources/definitions/malyan_m180.def.json
+++ b/resources/definitions/malyan_m180.def.json
@@ -25,8 +25,7 @@
"default_value": true
},
"machine_nozzle_size": {
- "default_value": 0.4,
- "minimum_value": "0.001"
+ "default_value": 0.4
},
"machine_head_with_fans_polygon": {
"default_value": [
@@ -36,6 +35,21 @@
[ 18, 35 ]
]
},
+ "machine_max_feedrate_z": {
+ "default_value": 400
+ },
+ "machine_steps_per_mm_x": {
+ "default_value": 93
+ },
+ "machine_steps_per_mm_y": {
+ "default_value": 93
+ },
+ "machine_steps_per_mm_z": {
+ "default_value": 1600
+ },
+ "machine_steps_per_mm_e": {
+ "default_value": 92
+ },
"gantry_height": {
"default_value": 55
},
diff --git a/resources/definitions/malyan_m200.def.json b/resources/definitions/malyan_m200.def.json
index 9aae3a5244..ec3237a7e6 100644
--- a/resources/definitions/malyan_m200.def.json
+++ b/resources/definitions/malyan_m200.def.json
@@ -11,7 +11,7 @@
"platform": "malyan_m200_platform.stl",
"has_machine_quality": true,
"has_materials": true,
- "preferred_quality": "*normal*",
+ "preferred_quality_type": "normal",
"supports_usb_connection": true,
"visible": true,
"first_start_actions": ["MachineSettingsAction"],
@@ -30,7 +30,12 @@
"speed_infill": { "value": "speed_print" },
"speed_topbottom": {"value": "speed_print / 2"},
- "layer_height": { "minimum_value": "0.04375", "maximum_value": "machine_nozzle_size * 0.875", "maximum_value_warning": "machine_nozzle_size * 0.48125 + 0.0875", "default_value": 0.13125 },
+ "layer_height":
+ {
+ "minimum_value_warning": "0.04375",
+ "maximum_value_warning": "machine_nozzle_size * 0.48125 + 0.0875",
+ "default_value": 0.13125
+ },
"line_width": { "value": "round(machine_nozzle_size * 0.875, 2)" },
"material_print_temperature": { "minimum_value": "0" },
@@ -51,7 +56,7 @@
"machine_height": { "default_value": 120 },
"machine_heated_bed": { "default_value": true },
"machine_center_is_zero": { "default_value": false },
- "material_diameter": { "value": 1.75 },
+ "material_diameter": { "default_value": 1.75 },
"machine_nozzle_size": {
"default_value": 0.4,
"minimum_value": 0.15
diff --git a/resources/definitions/monoprice_select_mini_v2.def.json b/resources/definitions/monoprice_select_mini_v2.def.json
index 87014c136b..99bb7ef50a 100644
--- a/resources/definitions/monoprice_select_mini_v2.def.json
+++ b/resources/definitions/monoprice_select_mini_v2.def.json
@@ -10,7 +10,7 @@
"file_formats": "text/x-gcode",
"has_machine_quality": true,
"has_materials": true,
- "preferred_quality": "*normal*",
+ "preferred_quality_type": "normal",
"visible": true
},
diff --git a/resources/definitions/peopoly_moai.def.json b/resources/definitions/peopoly_moai.def.json
index 78aca31dae..5a3cbddd14 100644
--- a/resources/definitions/peopoly_moai.def.json
+++ b/resources/definitions/peopoly_moai.def.json
@@ -136,7 +136,7 @@
},
"material_diameter": {
"enabled": false,
- "value": "1.75"
+ "default_value": 1.75
},
"cool_fan_enabled": {
"enabled": false,
diff --git a/resources/definitions/printrbot_simple_makers_kit.def.json b/resources/definitions/printrbot_simple_makers_kit.def.json
new file mode 100644
index 0000000000..e2afd57826
--- /dev/null
+++ b/resources/definitions/printrbot_simple_makers_kit.def.json
@@ -0,0 +1,38 @@
+{
+ "version": 2,
+ "name": "Printrbot Simple Maker's Kit (1405)",
+ "inherits": "fdmprinter",
+ "metadata": {
+ "visible": true,
+ "author": "Timur Tabi",
+ "manufacturer": "Printrbot",
+ "file_formats": "text/x-gcode"
+ },
+
+ "overrides": {
+ "machine_name": { "default_value": "Printrbot Simple Maker's Kit (1405)" },
+ "machine_heated_bed": { "default_value": false },
+ "machine_width": { "default_value": 100 },
+ "machine_depth": { "default_value": 100 },
+ "machine_height": { "default_value": 115 },
+ "material_diameter": { "default_value": 1.75 },
+ "machine_nozzle_size": { "default_value": 0.4 },
+ "machine_head_with_fans_polygon": {
+ "default_value": [
+ [-40, 1000],
+ [-40, -10],
+ [60, 1000],
+ [60, -10]
+ ]
+ },
+ "gantry_height": { "default_value": 1000 },
+ "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
+
+ "machine_start_gcode": {
+ "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;home X/Y\nG28 Z0 ;home Z\nG92 E0 ;zero the extruded length\nG29 ;initiate auto bed leveling sequence"
+ },
+ "machine_end_gcode": {
+ "default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nM106 S0 ;fan off\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit\nG1 Z+1 E-5 F9000 ;move Z up a bit and retract even more\nG28 X0 Y0 ;home X/Y, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
+ }
+ }
+}
diff --git a/resources/definitions/seemecnc_artemis.def.json b/resources/definitions/seemecnc_artemis.def.json
new file mode 100644
index 0000000000..0b31abfa41
--- /dev/null
+++ b/resources/definitions/seemecnc_artemis.def.json
@@ -0,0 +1,44 @@
+{
+ "version": 2,
+ "name": "SeeMeCNC Artemis",
+ "inherits": "fdmprinter",
+ "metadata": {
+ "visible": true,
+ "author": "PouncingIguana, JJ",
+ "manufacturer": "SeeMeCNC",
+ "file_formats": "text/x-gcode",
+ "icon": "icon_ultimaker2",
+ "platform": "artemis_platform.stl",
+ "has_materials": true
+ },
+
+ "overrides": {
+ "layer_height": { "default_value": 0.1618 },
+ "layer_height_0": { "default_value": 0.2 },
+ "machine_center_is_zero": { "default_value": true },
+ "machine_depth": { "default_value": 290 },
+ "machine_gcode_flavor": { "default_value": "RepRap (RepRap)" },
+ "machine_heated_bed": { "default_value": true },
+ "machine_height": { "default_value": 530 },
+ "machine_max_feedrate_z": { "default_value": 400 },
+ "machine_name": { "default_value": "Artemis" },
+ "machine_nozzle_size": { "default_value": 0.5 },
+ "machine_shape": { "default_value": "elliptic" },
+ "machine_width": { "default_value": 290 },
+ "material_diameter": { "default_value": 1.75 },
+ "relative_extrusion": { "default_value": false },
+ "retraction_amount": { "default_value": 3.2 },
+ "retraction_combing": { "default_value": "off" },
+ "retraction_hop_enabled": { "default_value": true },
+ "retraction_hop_only_when_collides": { "default_value": false },
+ "retraction_prime_speed": { "default_value": 45 },
+ "retraction_retract_speed": { "default_value": 45 },
+ "retraction_speed": { "default_value": 45 },
+ "machine_start_gcode": {
+ "default_value": "G28\nG1 Z15.0 F10000\nG92 E0"
+ },
+ "machine_end_gcode": {
+ "default_value": "M203 Z24000\nM104 S0\nM140 S0\nM107\nG28\nM84"
+ }
+ }
+}
diff --git a/resources/definitions/seemecnc_v32.def.json b/resources/definitions/seemecnc_v32.def.json
new file mode 100644
index 0000000000..3f46c1540a
--- /dev/null
+++ b/resources/definitions/seemecnc_v32.def.json
@@ -0,0 +1,44 @@
+{
+ "version": 2,
+ "name": "SeeMeCNC Rostock Max V3.2",
+ "inherits": "fdmprinter",
+ "metadata": {
+ "visible": true,
+ "author": "PouncingIguana, JJ",
+ "manufacturer": "SeeMeCNC",
+ "file_formats": "text/x-gcode",
+ "icon": "icon_ultimaker2",
+ "platform": "rostock_platform.stl",
+ "has_materials": true
+ },
+
+ "overrides": {
+ "layer_height": { "default_value": 0.1618 },
+ "layer_height_0": { "default_value": 0.2 },
+ "machine_center_is_zero": { "default_value": true },
+ "machine_depth": { "default_value": 265 },
+ "machine_gcode_flavor": { "default_value": "RepRap (RepRap)" },
+ "machine_heated_bed": { "default_value": true },
+ "machine_height": { "default_value": 395 },
+ "machine_max_feedrate_z": { "default_value": 300 },
+ "machine_name": { "default_value": "Rostock Max V3.2" },
+ "machine_nozzle_size": { "default_value": 0.5 },
+ "machine_shape": { "default_value": "elliptic" },
+ "machine_width": { "default_value": 265 },
+ "material_diameter": { "default_value": 1.75 },
+ "relative_extrusion": { "default_value": false },
+ "retraction_amount": { "default_value": 3.2 },
+ "retraction_combing": { "default_value": "off" },
+ "retraction_hop_enabled": { "default_value": true },
+ "retraction_hop_only_when_collides": { "default_value": false },
+ "retraction_prime_speed": { "default_value": 45 },
+ "retraction_retract_speed": { "default_value": 45 },
+ "retraction_speed": { "default_value": 45 },
+ "machine_start_gcode": {
+ "default_value": "G28\nG1 Z15.0 F10000\nG92 E0"
+ },
+ "machine_end_gcode": {
+ "default_value": "M203 Z24000\nM104 S0\nM140 S0\nM107\nG28\nM84"
+ }
+ }
+}
diff --git a/resources/definitions/tevo_blackwidow.def.json b/resources/definitions/tevo_blackwidow.def.json
index 04cadfb160..22f7095e17 100644
--- a/resources/definitions/tevo_blackwidow.def.json
+++ b/resources/definitions/tevo_blackwidow.def.json
@@ -11,7 +11,7 @@
"has_materials": false,
"has_machine_quality": true,
"platform": "tevo_blackwidow.stl",
- "preferred_quality": "*normal*"
+ "preferred_quality_type": "normal"
},
"overrides":
{
diff --git a/resources/definitions/tevo_tarantula.def.json b/resources/definitions/tevo_tarantula.def.json
index a9f9cefff2..c3bfb38192 100644
--- a/resources/definitions/tevo_tarantula.def.json
+++ b/resources/definitions/tevo_tarantula.def.json
@@ -2,7 +2,8 @@
"version": 2,
"name": "Tevo Tarantula",
"inherits": "fdmprinter",
- "metadata": {
+ "metadata":
+ {
"visible": true,
"author": "TheAssassin",
"manufacturer": "Tevo",
@@ -11,62 +12,39 @@
"platform": "prusai3_platform.stl"
},
- "overrides": {
- "machine_name": {
- "default_value": "Tevo Tarantula"
- },
- "machine_heated_bed": {
- "default_value": true
- },
- "machine_width": {
- "default_value": 200
- },
- "machine_height": {
- "default_value": 200
- },
- "machine_depth": {
- "default_value": 200
- },
- "machine_center_is_zero": {
- "default_value": false
- },
- "machine_nozzle_size": {
- "default_value": 0.4
- },
- "material_diameter": {
- "default_value": 1.75
- },
- "machine_head_polygon": {
- "default_value": [
+ "overrides":
+ {
+ "machine_name": { "default_value": "Tevo Tarantula" },
+ "machine_heated_bed": { "default_value": true },
+ "machine_width": { "default_value": 200 },
+ "machine_height": { "default_value": 200 },
+ "machine_depth": { "default_value": 200 },
+ "machine_center_is_zero": { "default_value": false },
+ "machine_nozzle_size": { "default_value": 0.4 },
+ "material_diameter": { "default_value": 1.75 },
+ "machine_head_polygon":
+ {
+ "default_value":
+ [
[-75, -18],
[-75, 35],
[18, 35],
[18, -18]
]
},
- "gantry_height": {
- "default_value": 55
- },
- "machine_gcode_flavor": {
- "default_value": "RepRap (Marlin/Sprinter)"
- },
- "machine_acceleration": {
- "default_value": 500
- },
- "machine_max_jerk_xy": {
- "default_value": 4.0
- },
- "machine_max_jerk_z": {
- "default_value": 0.2
- },
- "machine_max_jerk_e": {
- "default_value": 2.5
- },
- "machine_start_gcode": {
- "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..."
- },
- "machine_end_gcode": {
- "default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG90 ;absolute positioning\nG1 X0 Y200 F3600 ;move extruder out of the way by moving the baseplate to the front for easier access to printed object\nM84 ;steppers off"
- }
+ "gantry_height": { "default_value": 55 },
+ "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
+ "machine_acceleration": { "default_value": 2650 },
+ "machine_max_jerk_xy": { "default_value": 15.0 },
+ "machine_max_jerk_z": { "default_value": 0.4 },
+ "machine_max_jerk_e": { "default_value": 5 },
+ "machine_max_feedrate_x": { "default_value": 255 },
+ "machine_max_feedrate_y": { "default_value": 225 },
+ "machine_max_feedrate_z": { "default_value": 3 },
+ "machine_max_acceleration_x": { "default_value": 2620 },
+ "machine_max_acceleration_y": { "default_value": 2650 },
+ "acceleration_print": { "default_value": 2650 },
+ "machine_start_gcode": { "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." },
+ "machine_end_gcode": { "default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG90 ;absolute positioning\nG1 X0 Y200 F3600 ;move extruder out of the way by moving the baseplate to the front for easier access to printed object\nM84 ;steppers off" }
}
}
diff --git a/resources/definitions/ubuild-3d_mr_bot_280.def.json b/resources/definitions/ubuild-3d_mr_bot_280.def.json
new file mode 100644
index 0000000000..4febdcd350
--- /dev/null
+++ b/resources/definitions/ubuild-3d_mr_bot_280.def.json
@@ -0,0 +1,49 @@
+{
+ "id": "ubuild-3d_mr_bot_280",
+ "version": 2,
+ "name": "uBuild-3D Mr Bot 280",
+ "inherits": "fdmprinter",
+ "metadata": {
+ "visible": true,
+ "author": "uBuild-3D",
+ "manufacturer": "uBuild-3D",
+ "category": "Other",
+ "file_formats": "text/x-gcode",
+ "icon": "icon_uBuild-3D",
+ "platform": "mr_bot_280_platform.stl",
+ "has_materials": true,
+ "preferred_quality_type": "draft"
+ },
+
+ "overrides": {
+ "machine_name": { "default_value": "Mr Bot 280" },
+ "machine_heated_bed": { "default_value": true },
+ "machine_width": { "default_value": 275 },
+ "machine_height": { "default_value": 275 },
+ "machine_depth": { "default_value": 275 },
+ "machine_center_is_zero": { "default_value": false },
+ "material_diameter": { "default_value": 1.75 },
+ "material_bed_temperature": { "default_value": 70 },
+ "machine_nozzle_size": { "default_value": 0.4 },
+ "layer_height_0": { "default_value": 0.1 },
+ "retraction_amount": { "default_value": 2 },
+ "retraction_speed": { "default_value": 50 },
+ "retraction_retract_speed": { "default_value": 50 },
+ "retraction_prime_speed": { "default_value": 30 },
+ "adhesion_type": { "default_value": "skirt" },
+ "machine_nozzle_heat_up_speed": { "default_value": 2 },
+ "machine_nozzle_cool_down_speed": { "default_value": 2 },
+ "machine_head_with_fans_polygon": { "default_value": [[-20,20],[10,10],[10,10],[10,10]] },
+ "gantry_height": { "default_value": 275 },
+ "machine_max_feedrate_z": { "default_value": 15 },
+ "machine_max_feedrate_e": { "default_value": 60 },
+ "machine_max_acceleration_z": { "default_value": 1000 },
+ "machine_acceleration": { "default_value": 2000 },
+ "machine_max_jerk_xy": { "default_value": 20 },
+ "machine_max_jerk_z": { "default_value": 0.4 },
+ "machine_max_jerk_e": { "default_value": 5 },
+ "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
+ "machine_start_gcode": { "default_value": "G21 ; set units to millimeters\nG90 ; use absolute positioning\nM82 ; absolute extrusion mode\nM140 S{material_bed_temperature} ; set bed temp\nM104 S{material_print_temperature} ; set extruder temp\nG28 ; home X, Y and Z\nG29 ; probe sequence (for auto-leveling)\nG1 Z15 F600 ; go to Z15 position\nG1 X0 Y-20 F10000 ; go to X0 Y-20 position\nM190 S{material_bed_temperature} ; wait for bed temp\nM109 S{material_print_temperature} ; wait for extruder temp\nG92 E0 ; reset extruder distance position\nG1 E25 F100 ; extrude 25mm of material\nG92 E0 ; reset extruder distance position\nM117 Printing..." },
+ "machine_end_gcode": { "default_value": "M400 ; wait for moves to finish\nG92 Z0 E0 ; reset Z position\nG1 E-2 F9000 ; retract material\nG1 Z2 ; get extruder out of the way\nM104 S0 ; turn off extruder\nG1 Y285 F3000 ; present finished print\nM140 S0 ; turn off bed\nM84 ; disable motors\nM117 Print complete" }
+ }
+}
diff --git a/resources/definitions/ultimaker2.def.json b/resources/definitions/ultimaker2.def.json
index 038071574f..33b48116be 100644
--- a/resources/definitions/ultimaker2.def.json
+++ b/resources/definitions/ultimaker2.def.json
@@ -20,12 +20,10 @@
"overrides": {
"machine_name": { "default_value": "Ultimaker 2" },
"machine_start_gcode" : {
- "default_value": "",
"value": "\"\" if machine_gcode_flavor == \"UltiGCode\" else \"G21 ;metric values\\nG90 ;absolute positioning\\nM82 ;set extruder to absolute mode\\nM107 ;start with the fan off\\nG28 Z0 ;move Z to bottom endstops\\nG28 X0 Y0 ;move X/Y to endstops\\nG1 X15 Y0 F4000 ;move X/Y to front of printer\\nG1 Z15.0 F9000 ;move the platform to 15mm\\nG92 E0 ;zero the extruded length\\nG1 F200 E10 ;extrude 10 mm of feed stock\\nG92 E0 ;zero the extruded length again\\nG1 F9000\\n;Put printing message on LCD screen\\nM117 Printing...\""
},
"machine_end_gcode" : {
- "default_value": "",
- "value": "\"\" if machine_gcode_flavor == \"UltiGCode\" else \"M104 S0 ;extruder heater off\\nM140 S0 ;heated bed heater off (if you have it)\\nG91 ;relative positioning\\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\\nM84 ;steppers off\\nG90 ;absolute positioning\""
+ "value": "\";Version _2.6 of the firmware can abort the print too early if the file ends\\n;too soon. However if the file hasn't ended yet because there are comments at\\n;the end of the file, it won't abort yet. Therefore we have to put at least 512\\n;bytes at the end of the g-code so that the file is not yet finished by the\\n;time that the motion planner gets flushed. With firmware version _3.3 this\\n;should be fixed, so this comment wouldn't be necessary any more. Now we have\\n;to pad this text to make precisely 512 bytes.\" if machine_gcode_flavor == \"UltiGCode\" else \"M104 S0 ;extruder heater off\\nM140 S0 ;heated bed heater off (if you have it)\\nG91 ;relative positioning\\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\\nM84 ;steppers off\\nG90 ;absolute positioning\\n;Version _2.6 of the firmware can abort the print too early if the file ends\\n;too soon. However if the file hasn't ended yet because there are comments at\\n;the end of the file, it won't abort yet. Therefore we have to put at least 512\\n;bytes at the end of the g-code so that the file is not yet finished by the\\n;time that the motion planner gets flushed. With firmware version _3.3 this\\n;should be fixed, so this comment wouldn't be necessary any more. Now we have\\n;to pad this text to make precisely 512 bytes.\""
},
"machine_width": {
"default_value": 223
diff --git a/resources/definitions/ultimaker2_plus.def.json b/resources/definitions/ultimaker2_plus.def.json
index 58833904d2..935bf5b6c0 100644
--- a/resources/definitions/ultimaker2_plus.def.json
+++ b/resources/definitions/ultimaker2_plus.def.json
@@ -9,7 +9,7 @@
"file_formats": "text/x-gcode",
"platform": "ultimaker2_platform.obj",
"platform_texture": "Ultimaker2Plusbackplate.png",
- "preferred_variant": "*0.4*",
+ "preferred_variant_name": "0.4 mm",
"has_variants": true,
"has_materials": true,
"has_machine_materials": true,
diff --git a/resources/definitions/ultimaker3.def.json b/resources/definitions/ultimaker3.def.json
index e7b9b6255e..05f74c6342 100644
--- a/resources/definitions/ultimaker3.def.json
+++ b/resources/definitions/ultimaker3.def.json
@@ -6,7 +6,7 @@
"author": "Ultimaker",
"manufacturer": "Ultimaker B.V.",
"visible": true,
- "file_formats": "text/x-gcode",
+ "file_formats": "application/gzip;text/x-gcode",
"platform": "ultimaker3_platform.obj",
"platform_texture": "Ultimaker3backplate.png",
"platform_offset": [0, 0, 0],
@@ -15,8 +15,8 @@
"has_machine_materials": true,
"has_variant_materials": true,
"has_variants": true,
- "preferred_variant": "*aa04*",
- "preferred_quality": "*Normal*",
+ "preferred_variant_name": "AA 0.4",
+ "preferred_quality_type": "normal",
"variants_name": "Print core",
"machine_extruder_trains":
{
@@ -43,10 +43,10 @@
{
"default_value":
[
- [ -29, 6.1 ],
- [ -29, -33.9 ],
- [ 71, 6.1 ],
- [ 71, -33.9 ]
+ [ -41.9, -45.8 ],
+ [ -41.9, 33.9 ],
+ [ 59.9, 33.9 ],
+ [ 59.9, -45.8 ]
]
},
"machine_gcode_flavor": { "default_value": "Griffin" },
@@ -90,6 +90,7 @@
"infill_overlap": { "value": "0" },
"infill_pattern": { "value": "'triangles'" },
"infill_wipe_dist": { "value": "0" },
+ "initial_layer_line_width_factor": { "value": "120" },
"jerk_enabled": { "value": "True" },
"jerk_layer_0": { "value": "jerk_topbottom" },
"jerk_prime_tower": { "value": "math.ceil(jerk_print * 15 / 25)" },
@@ -109,7 +110,9 @@
"material_bed_temperature": { "maximum_value": "115" },
"material_bed_temperature_layer_0": { "maximum_value": "115" },
"material_standby_temperature": { "value": "100" },
+ "meshfix_maximum_resolution": { "value": "0.04" },
"multiple_mesh_overlap": { "value": "0" },
+ "optimize_wall_printing_order": { "value": "True" },
"prime_tower_enable": { "default_value": true },
"raft_airgap": { "value": "0" },
"raft_base_thickness": { "value": "0.3" },
@@ -123,7 +126,7 @@
"retraction_count_max": { "value": "10" },
"retraction_extrusion_window": { "value": "1" },
"retraction_hop": { "value": "2" },
- "retraction_hop_enabled": { "value": "True" },
+ "retraction_hop_enabled": { "value": "extruders_enabled_count > 1" },
"retraction_hop_only_when_collides": { "value": "True" },
"retraction_min_travel": { "value": "5" },
"retraction_prime_speed": { "value": "15" },
@@ -147,9 +150,10 @@
"switch_extruder_prime_speed": { "value": "15" },
"switch_extruder_retraction_amount": { "value": "8" },
"top_bottom_thickness": { "value": "1" },
- "travel_avoid_distance": { "value": "3" },
+ "travel_avoid_distance": { "value": "3 if extruders_enabled_count > 1 else machine_nozzle_tip_outer_diameter / 2 * 1.5" },
"wall_0_inset": { "value": "0" },
"wall_line_width_x": { "value": "round(wall_line_width * 0.3 / 0.35, 2)" },
- "wall_thickness": { "value": "1" }
+ "wall_thickness": { "value": "1" },
+ "zig_zaggify_infill": { "value": "True" }
}
}
diff --git a/resources/definitions/ultimaker3_extended.def.json b/resources/definitions/ultimaker3_extended.def.json
index 385199f4f1..1e6c322c73 100644
--- a/resources/definitions/ultimaker3_extended.def.json
+++ b/resources/definitions/ultimaker3_extended.def.json
@@ -7,7 +7,7 @@
"manufacturer": "Ultimaker B.V.",
"quality_definition": "ultimaker3",
"visible": true,
- "file_formats": "text/x-gcode",
+ "file_formats": "application/gzip;text/x-gcode",
"platform": "ultimaker3_platform.obj",
"platform_texture": "Ultimaker3Extendedbackplate.png",
"platform_offset": [0, 0, 0],
@@ -16,7 +16,7 @@
"has_variant_materials": true,
"has_materials": true,
"has_variants": true,
- "preferred_variant": "*aa04*",
+ "preferred_variant_name": "AA 0.4",
"variants_name": "Print core",
"machine_extruder_trains":
{
diff --git a/resources/definitions/vertex_delta_k8800.def.json b/resources/definitions/vertex_delta_k8800.def.json
index 736d17e17a..495fd5a5bc 100644
--- a/resources/definitions/vertex_delta_k8800.def.json
+++ b/resources/definitions/vertex_delta_k8800.def.json
@@ -6,7 +6,9 @@
"manufacturer": "Velleman nv",
"file_formats": "text/x-gcode",
"visible": true,
- "author": "Velleman"
+ "author": "Velleman",
+ "has_machine_quality": true,
+ "has_materials": true
},
"overrides": {
"material_diameter": {
@@ -59,6 +61,99 @@
},
"machine_end_gcode": {
"default_value": "; Vertex Delta end code\nM107 ; Turn off fan\nG91 ; Relative positioning\nT0\nG1 E-1 F1500; Reduce filament pressure\nM104 T0 S0\nG90 ; Absolute positioning\nG92 E0 ; Reset extruder position\nM300 S4000 P500\nM300 S3000 P500\nM300 S2000 P800\nG28\nM84 ; Turn steppers off"
+ },
+ "line_width": {
+ "value": 0.35
+ },
+ "infill_line_width": {
+ "value": 0.35
+ },
+ "wall_thickness": {
+ "value": 0.7
+ },
+ "top_bottom_thickness": {
+ "value": 0.6
+ },
+ "infill_sparse_density": {
+ "value": 40
+ },
+ "infill_overlap": {
+ "value": 5
+ },
+ "min_infill_area": {
+ "value": 0.1
+ },
+ "retract_at_layer_change": {
+ "value": true
+ },
+ "retraction_min_travel": {
+ "value": 1
+ },
+ "retraction_count_max": {
+ "value": 15
+ },
+ "retraction_extrusion_window": {
+ "value": 1
+ },
+ "speed_print": {
+ "value": 35
+ },
+ "speed_infill": {
+ "value": 40
+ },
+ "speed_wall": {
+ "value": 35
+ },
+ "speed_wall_x": {
+ "value": 35
+ },
+ "speed_topbottom": {
+ "value": 35
+ },
+ "speed_travel": {
+ "value": 190
+ },
+ "speed_layer_0": {
+ "value": 20
+ },
+ "speed_print_layer_0": {
+ "value": 20
+ },
+ "skirt_brim_speed": {
+ "value": 20
+ },
+ "travel_retract_before_outer_wall": {
+ "value": false
+ },
+ "retraction_hop_enabled": {
+ "value": true
+ },
+ "retraction_hop": {
+ "value": 0.1
+ },
+ "cool_fan_full_at_height": {
+ "value": 2
+ },
+ "cool_fan_full_layer": {
+ "value": 11
+ },
+ "cool_min_layer_time": {
+ "value": 8
+ },
+ "support_z_distance": {
+ "value": 0.4
+ },
+ "support_xy_distance": {
+ "value": 1
+ },
+ "brim_width": {
+ "value": 6
+ },
+ "skirt_line_count": {
+ "value": 2
+ },
+ "skirt_brim_minimal_length": {
+ "value": 50
}
}
}
\ No newline at end of file
diff --git a/resources/extruders/cartesio_extruder_0.def.json b/resources/extruders/cartesio_extruder_0.def.json
index 5558d9325e..47b5b5abf5 100644
--- a/resources/extruders/cartesio_extruder_0.def.json
+++ b/resources/extruders/cartesio_extruder_0.def.json
@@ -16,17 +16,10 @@
"machine_nozzle_offset_x": { "default_value": 0.0 },
"machine_nozzle_offset_y": { "default_value": 0.0 },
"machine_extruder_start_code": {
- "default_value": "\n;start extruder_0\n\nM117 printing...\n"
+ "default_value": "\n;start T0\n\nM104 T0 S{material_print_temperature_layer_0}\nG1 X65 Y35 F9000 ; go to wipe position\nM109 T0 S{material_print_temperature_layer_0}; wait for temp\nG1 E10 F300; prime\nG92 E0\nG1 X45 Y15 F3000; wipe\nG1 X55 F9000\nG1 Y35 F6000; wipe again\n\nM117 printing...\n"
},
"machine_extruder_end_code": {
- "default_value": "\nM104 T0 S120\n;end extruder_0\nM117 temp is {material_print_temp}\n"
- },
-
- "machine_extruder_start_pos_abs": { "default_value": true },
- "machine_extruder_start_pos_x": { "default_value": 24 },
- "machine_extruder_start_pos_y": { "default_value": 16 },
- "machine_extruder_end_pos_abs": { "default_value": true },
- "machine_extruder_end_pos_x": { "default_value": 48 },
- "machine_extruder_end_pos_y": { "default_value": 16 }
+ "default_value": "\nM104 T0 S{material_standby_temperature}\nG1 X65 Y35 F9000 ; go to wipe position\nM109 T0 R{material_standby_temperature}; wait for temp\nG1 X45 Y15 F3000; wipe\nG1 X55 F9000\nG1 Y35 F6000; wipe again\n\n;end T0\n\n"
+ }
}
}
diff --git a/resources/extruders/cartesio_extruder_1.def.json b/resources/extruders/cartesio_extruder_1.def.json
index f8350f8091..78bcccd12a 100644
--- a/resources/extruders/cartesio_extruder_1.def.json
+++ b/resources/extruders/cartesio_extruder_1.def.json
@@ -16,17 +16,10 @@
"machine_nozzle_offset_x": { "default_value": 24.0 },
"machine_nozzle_offset_y": { "default_value": 0.0 },
"machine_extruder_start_code": {
- "default_value": "\n;start extruder_1\n\nM117 printing...\n"
+ "default_value": "\n;start T1\n\nM104 T1 S{material_print_temperature_layer_0}\nG1 X41 Y35 F9000 ; go to wipe position\nM109 T1 S{material_print_temperature_layer_0}; wait for temp\nG1 E10 F300; prime\nG92 E0\nG1 X21 Y15 F3000; wipe\nG1 X34 F9000\nG1 Y35 F6000; wipe again\n\nM117 printing...\n"
},
"machine_extruder_end_code": {
- "default_value": "\nM104 T1 S120\n;end extruder_1\n"
- },
-
- "machine_extruder_start_pos_abs": { "default_value": true },
- "machine_extruder_start_pos_x": { "default_value": 48 },
- "machine_extruder_start_pos_y": { "default_value": 16 },
- "machine_extruder_end_pos_abs": { "default_value": true },
- "machine_extruder_end_pos_x": { "default_value": 24 },
- "machine_extruder_end_pos_y": { "default_value": 16 }
+ "default_value": "\nM104 T1 S{material_standby_temperature}\nG1 X41 Y35 F9000 ; go to wipe position\nM109 T1 R{material_standby_temperature}; wait for temp\nG1 X21 Y15 F3000; wipe\nG1 X31 F9000\nG1 Y35 F6000; wipe again\n\n;end T1\n\n"
+ }
}
}
diff --git a/resources/extruders/cartesio_extruder_2.def.json b/resources/extruders/cartesio_extruder_2.def.json
index bfc10e75c3..dbd6643bfe 100644
--- a/resources/extruders/cartesio_extruder_2.def.json
+++ b/resources/extruders/cartesio_extruder_2.def.json
@@ -13,20 +13,13 @@
"default_value": 2,
"maximum_value": "3"
},
- "machine_nozzle_offset_x": { "default_value": 0.0 },
- "machine_nozzle_offset_y": { "default_value": 0.0 },
+ "machine_nozzle_offset_x": { "default_value": 24.0 },
+ "machine_nozzle_offset_y": { "default_value": -100.0 },
"machine_extruder_start_code": {
- "default_value": "\n;start extruder_2\n\nM117 printing...\n"
+ "default_value": "\n;start T2\n\nM104 T2 S{material_print_temperature_layer_0}\nG1 X41 Y215 F9000 ; go to wipe position\nM109 T2 S{material_print_temperature_layer_0}; wait for temp\nG1 E10 F300; prime\nG92 E0\nG1 X21 Y235 F3000; wipe\nG1 X31 F9000\nG1 Y215 F6000; wipe again\n\nM117 printing...\n"
},
"machine_extruder_end_code": {
- "default_value": "\nM104 T2 S120\n;end extruder_2\n"
- },
-
- "machine_extruder_start_pos_abs": { "default_value": true },
- "machine_extruder_start_pos_x": { "default_value": 24 },
- "machine_extruder_start_pos_y": { "default_value": 309 },
- "machine_extruder_end_pos_abs": { "default_value": true },
- "machine_extruder_end_pos_x": { "default_value": 48 },
- "machine_extruder_end_pos_y": { "default_value": 309 }
+ "default_value": "\nM104 T2 S{material_standby_temperature}\nG1 X41 Y215 F9000 ; go to wipe position\nM109 T2 R{material_standby_temperature}; wait for temp\nG1 X21 Y235 F3000; wipe\nG1 X31 F9000\nG1 Y215 F6000; wipe again\n\n;end T2\n\n"
+ }
}
}
diff --git a/resources/extruders/cartesio_extruder_3.def.json b/resources/extruders/cartesio_extruder_3.def.json
index f0be53e564..beed117abe 100644
--- a/resources/extruders/cartesio_extruder_3.def.json
+++ b/resources/extruders/cartesio_extruder_3.def.json
@@ -14,19 +14,12 @@
"maximum_value": "3"
},
"machine_nozzle_offset_x": { "default_value": 0.0 },
- "machine_nozzle_offset_y": { "default_value": 0.0 },
+ "machine_nozzle_offset_y": { "default_value": -100.0 },
"machine_extruder_start_code": {
- "default_value": "\n;start extruder_3\n\nM117 printing...\n"
+ "default_value": "\n;start T3\n\nM104 T3 S{material_print_temperature_layer_0}\nG1 X65 Y215 F9000 ; go to wipe position\nM109 T3 S{material_print_temperature_layer_0}; wait for temp\nG1 E10 F300; prime\nG92 E0\nG1 X45 Y235 F3000; wipe\nG1 X55 F9000\nG1 Y215 F6000; wipe again\n\nM117 printing...\n"
},
"machine_extruder_end_code": {
- "default_value": "\nM104 T3 S120\n;end extruder_3\n"
- },
-
- "machine_extruder_start_pos_abs": { "default_value": true },
- "machine_extruder_start_pos_x": { "default_value": 48 },
- "machine_extruder_start_pos_y": { "default_value": 309 },
- "machine_extruder_end_pos_abs": { "default_value": true },
- "machine_extruder_end_pos_x": { "default_value": 24 },
- "machine_extruder_end_pos_y": { "default_value": 309}
+ "default_value": "\nM104 T3 S{material_standby_temperature}\nG1 X65 Y215 F9000 ; go to wipe position\nM109 T3 R{material_standby_temperature}; wait for temp\nG1 X45 Y235 F3000; wipe\nG1 X55 F9000\nG1 Y215 F6000; wipe again\n\n;end T3\n\n"
+ }
}
}
diff --git a/resources/extruders/punchtec_connect_xl_extruder_left.def.json b/resources/extruders/punchtec_connect_xl_extruder_0.def.json
similarity index 100%
rename from resources/extruders/punchtec_connect_xl_extruder_left.def.json
rename to resources/extruders/punchtec_connect_xl_extruder_0.def.json
diff --git a/resources/extruders/punchtec_connect_xl_extruder_right.def.json b/resources/extruders/punchtec_connect_xl_extruder_1.def.json
similarity index 100%
rename from resources/extruders/punchtec_connect_xl_extruder_right.def.json
rename to resources/extruders/punchtec_connect_xl_extruder_1.def.json
diff --git a/resources/i18n/cura.pot b/resources/i18n/cura.pot
index 08d81f0b00..1620e11d81 100644
--- a/resources/i18n/cura.pot
+++ b/resources/i18n/cura.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: TEAM\n"
@@ -3598,6 +3598,11 @@ msgctxt "@label"
msgid "SVG icons"
msgstr ""
+#: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:139
+msgctxt "@label"
+msgid "Linux cross-distribution application deployment"
+msgstr ""
+
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:41
msgctxt "@label"
msgid "Profile:"
diff --git a/resources/i18n/de_DE/cura.po b/resources/i18n/de_DE/cura.po
index 6ade867317..250e3e5e1b 100644
--- a/resources/i18n/de_DE/cura.po
+++ b/resources/i18n/de_DE/cura.po
@@ -1,20 +1,22 @@
# Cura
-# Copyright (C) 2017 Ultimaker
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
-"PO-Revision-Date: 2017-11-30 13:05+0100\n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
+"PO-Revision-Date: 2018-02-12 13:25+0100\n"
"Last-Translator: Bothof \n"
"Language-Team: German\n"
"Language: de_DE\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.0.4\n"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:26
msgctxt "@action"
@@ -145,7 +147,7 @@ msgstr "Drucker nicht verfügbar"
#: /home/ruben/Projects/Cura/plugins/USBPrinting/USBPrinterOutputDevice.py:485
msgctxt "@info:status"
msgid "This printer does not support USB printing because it uses UltiGCode flavor."
-msgstr "Der Drucker unterstützt keinen USB-Druck, da er die UltiGCode-Variante verwendet."
+msgstr "Der Drucker unterstützt keinen USB-Druck, da er UltiGCode verwendet."
#: /home/ruben/Projects/Cura/plugins/USBPrinting/USBPrinterOutputDevice.py:485
msgctxt "@info:title"
@@ -187,7 +189,7 @@ msgstr "Drucker-Firmware"
#: /home/ruben/Projects/Cura/plugins/PrepareStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Prepare"
-msgstr ""
+msgstr "Vorbereiten"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:23
msgctxt "@action:button Preceded by 'Ready to'."
@@ -294,7 +296,7 @@ msgstr "Drucken über Netzwerk"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:110
msgctxt "@properties:tooltip"
msgid "Print over network"
-msgstr "Drücken über Netzwerk"
+msgstr "Drucken über Netzwerk"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:159
msgctxt "@info:status"
@@ -560,7 +562,7 @@ msgstr "Öffnet die Schaltfläche für Druckaufträge in Ihrem Browser."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:239
msgctxt "@label Printer name"
msgid "Unknown"
-msgstr ""
+msgstr "Unbekannt"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:505
#, python-brace-format
@@ -595,7 +597,7 @@ msgstr "Anschluss über Netzwerk"
#: /home/ruben/Projects/Cura/plugins/MonitorStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Monitor"
-msgstr ""
+msgstr "Überwachen"
#: /home/ruben/Projects/Cura/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py:66
#, python-brace-format
@@ -622,7 +624,7 @@ msgstr "Zugriff auf Update-Informationen nicht möglich."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:579
msgctxt "@info:status"
msgid "SolidWorks reported errors, while opening your file. We recommend to solve these issues inside SolidWorks itself."
-msgstr ""
+msgstr "SolidWorks hat beim Öffnen Ihrer Datei Fehler gemeldet. Wir empfehlen, diese Probleme direkt in SolidWorks zu lösen."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:591
msgctxt "@info:status"
@@ -631,6 +633,9 @@ msgid ""
"\n"
" Thanks!."
msgstr ""
+"Keine Modelle in Ihrer Zeichnung gefunden. Bitte überprüfen Sie den Inhalt erneut und stellen Sie sicher, dass ein Teil oder eine Baugruppe enthalten ist.\n"
+"\n"
+" Danke!"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:595
msgctxt "@info:status"
@@ -639,6 +644,9 @@ msgid ""
"\n"
"Sorry!"
msgstr ""
+"Es wurde mehr als ein Teil oder eine Baugruppe in Ihrer Zeichnung gefunden. Wir unterstützen derzeit nur Zeichnungen mit exakt einem Teil oder einer Baugruppe.\n"
+"\n"
+"Es tut uns leid!"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:25
msgctxt "@item:inlistbox"
@@ -653,7 +661,7 @@ msgstr "SolidWorks Einbaudatei"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:33
msgctxt "@item:inlistbox"
msgid "SolidWorks drawing file"
-msgstr ""
+msgstr "SolidWorks Zeichnungsdatei"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:48
msgctxt "@info:status"
@@ -664,6 +672,11 @@ msgid ""
"With kind regards\n"
" - Thomas Karl Pietrowski"
msgstr ""
+"Sehr geehrter Kunde,\n"
+"wir konnten keine gültige Installation von SolidWorks auf Ihrem System finden. Das bedeutet, dass SolidWorks entweder nicht installiert ist oder sie keine gültige Lizenz besitzen. Stellen Sie bitte sicher, dass SolidWorks problemlos läuft und/oder wenden Sie sich an Ihre ICT-Abteilung.\n"
+"\n"
+"Mit freundlichen Grüßen\n"
+" - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:57
msgctxt "@info:status"
@@ -674,6 +687,11 @@ msgid ""
"With kind regards\n"
" - Thomas Karl Pietrowski"
msgstr ""
+"Sehr geehrter Kunde,\n"
+"Sie verwenden dieses Plugin derzeit auf einem anderen Betriebssystem als Windows. Dieses Plugin funktioniert nur auf Windows mit installiertem SolidWorks und einer gültigen Lizenz. Installieren Sie dieses Plugin bitte auf einem Windows-Rechner mit installiertem SolidWorks.\n"
+"\n"
+"Mit freundlichen Grüßen\n"
+" - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:70
msgid "Configure"
@@ -681,7 +699,7 @@ msgstr "Konfigurieren"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:71
msgid "Installation guide for SolidWorks macro"
-msgstr ""
+msgstr "Installationsanleitung für SolidWorks Makro"
#: /home/ruben/Projects/Cura/plugins/SimulationView/__init__.py:14
msgctxt "@item:inlistbox"
@@ -705,7 +723,7 @@ msgstr "G-Code ändern"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:43
msgctxt "@info"
msgid "Cura collects anonymized usage statistics."
-msgstr ""
+msgstr "Cura erfasst anonymisierte Nutzungsstatistiken."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:46
msgctxt "@info:title"
@@ -715,22 +733,22 @@ msgstr "Daten werden erfasst"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:48
msgctxt "@action:button"
msgid "Allow"
-msgstr ""
+msgstr "Zulassen"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:49
msgctxt "@action:tooltip"
msgid "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing."
-msgstr ""
+msgstr "Damit lassen Sie zu, dass Cura anonymisierte Nutzungsstatistiken sendet, um zukünftige Verbesserungen für Cura zu definieren. Einige Ihrer Präferenzen und Einstellungen, die Cura-Version und ein Hash der Modelle, die Sie slicen, werden gesendet."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:50
msgctxt "@action:button"
msgid "Disable"
-msgstr ""
+msgstr "Deaktivieren"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:51
msgctxt "@action:tooltip"
msgid "Don't allow Cura to send anonymized usage statistics. You can enable it again in the preferences."
-msgstr ""
+msgstr "Damit wird Cura die Erlaubnis zum Senden von anonymisierten Nutzungsstatistiken entzogen. Sie können die Erlaubnis in den Einstellungen erneut aktivieren."
#: /home/ruben/Projects/Cura/plugins/LegacyProfileReader/__init__.py:14
msgctxt "@item:inlistbox"
@@ -740,7 +758,7 @@ msgstr "Cura 15.04-Profile"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/__init__.py:15
msgctxt "@item:inlistbox"
msgid "Blender file"
-msgstr ""
+msgstr "Blender-Datei"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/CadIntegrationUtils/CommonReader.py:199
msgctxt "@info:status"
@@ -748,6 +766,8 @@ msgid ""
"Could not export using \"{}\" quality!\n"
"Felt back to \"{}\"."
msgstr ""
+"Exportieren in \"{}\" Qualität nicht möglich!\n"
+"Zurückgeschaltet auf \"{}\"."
#: /home/ruben/Projects/Cura/plugins/GCodeProfileReader/__init__.py:14
#: /home/ruben/Projects/Cura/plugins/GCodeReader/__init__.py:14
@@ -934,12 +954,12 @@ msgstr "Cura-Profil"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:12
msgctxt "@item:inmenu"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Profilassistent"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:17
msgctxt "@item:inlistbox"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Profilassistent"
#: /home/ruben/Projects/Cura/plugins/3MFWriter/__init__.py:30
msgctxt "@item:inlistbox"
@@ -1137,13 +1157,13 @@ msgstr "Import des Profils aus Datei {0} fehlgeschlagen: or !"
msgid "This profile {0} contains incorrect data, could not import it."
-msgstr ""
+msgstr "Dieses Profil {0} enthält falsche Daten, Importieren nicht möglich."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:240
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "The machine defined in profile {0} doesn't match with your current machine, could not import it."
-msgstr ""
+msgstr "Die Maschine, die im Profil {0} definiert wurde, entspricht nicht Ihrer derzeitigen Maschine. Importieren nicht möglich."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:274
#, python-brace-format
@@ -1155,7 +1175,7 @@ msgstr "Profil erfolgreich importiert {0}"
#, python-brace-format
msgctxt "@info:status"
msgid "File {0} does not contain any valid profile."
-msgstr ""
+msgstr "Datei {0} enthält kein gültiges Profil."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:280
#, python-brace-format
@@ -1183,7 +1203,7 @@ msgstr "Es konnte keine Qualitätsangabe {0} für die vorliegende Konfiguration
#, python-brace-format
msgctxt "@label"
msgid "Group #{group_nr}"
-msgstr ""
+msgstr "Gruppe #{group_nr}"
#: /home/ruben/Projects/Cura/cura/BuildVolume.py:100
msgctxt "@info:status"
@@ -1243,6 +1263,9 @@ msgid ""
"
Please use the \"Send report\" button to post a bug report automatically to our servers
\n"
" "
msgstr ""
+"
Ein schwerer Fehler ist aufgetreten. Senden Sie uns diesen Absturzbericht, um das Problem zu beheben
\n"
+"
Verwenden Sie bitte die Schaltfläche „Bericht senden“, um den Fehlerbericht automatisch an unsere Server zu senden
"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:147
msgctxt "@title:groupbox"
msgid "Error traceback"
-msgstr ""
+msgstr "Fehler-Rückverfolgung"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:214
msgctxt "@title:groupbox"
@@ -1516,7 +1539,7 @@ msgstr "Düsengröße"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:393
msgctxt "@label"
msgid "Compatible material diameter"
-msgstr ""
+msgstr "Kompatibler Materialdurchmesser"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:395
msgctxt "@tooltip"
@@ -1610,7 +1633,7 @@ msgstr "Unbekannter Fehlercode: %1"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:55
msgctxt "@title:window"
msgid "Connect to Networked Printer"
-msgstr "Anschluss an vernetzten Drucker"
+msgstr "Anschluss an Netzwerk Drucker"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:65
msgctxt "@label"
@@ -1661,12 +1684,12 @@ msgstr "Typ"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:233
msgctxt "@label Printer name"
msgid "Ultimaker 3"
-msgstr ""
+msgstr "Ultimaker 3"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:236
msgctxt "@label Printer name"
msgid "Ultimaker 3 Extended"
-msgstr ""
+msgstr "Ultimaker 3 Extended"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:252
msgctxt "@label"
@@ -1733,7 +1756,7 @@ msgstr "%1 ist nicht für das Hosten einer Gruppe verbundener Ultimaker 3-Drucke
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml:55
msgctxt "@label link to connect manager"
msgid "Add/Remove printers"
-msgstr ""
+msgstr "Drucker hinzufügen/entfernen"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/OpenPanelButton.qml:14
msgctxt "@info:tooltip"
@@ -1773,7 +1796,7 @@ msgstr "Verbindung zum Drucker wurde unterbrochen"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:47
msgctxt "@label Printer status"
msgid "Unknown"
-msgstr ""
+msgstr "Unbekannt"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:257
msgctxt "@label:status"
@@ -1869,62 +1892,62 @@ msgstr "Konfiguration aktivieren"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:21
msgctxt "@title:window"
msgid "SolidWorks: Export wizard"
-msgstr ""
+msgstr "SolidWorks: Exportassistent"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:45
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:140
msgctxt "@action:label"
msgid "Quality:"
-msgstr ""
+msgstr "Qualität:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:78
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:179
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (3D-printing)"
-msgstr ""
+msgstr "Fein (3D-Druck)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:79
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:180
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (3D-printing)"
-msgstr ""
+msgstr "Grob (3D-Druck)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:80
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:181
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (SolidWorks)"
-msgstr ""
+msgstr "Fein (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:81
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:182
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (SolidWorks)"
-msgstr ""
+msgstr "Grob (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:94
msgctxt "@text:window"
msgid "Show this dialog again"
-msgstr ""
+msgstr "Diesen Dialog erneut anzeigen"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:104
msgctxt "@action:button"
msgid "Continue"
-msgstr ""
+msgstr "Weiter"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:116
msgctxt "@action:button"
msgid "Abort"
-msgstr ""
+msgstr "Abbrechen"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:21
msgctxt "@title:window"
msgid "How to install Cura SolidWorks macro"
-msgstr ""
+msgstr "Installieren von Cura SolidWorks Makro"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:62
msgctxt "@description:label"
msgid "Steps:"
-msgstr ""
+msgstr "Schritte:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:140
msgctxt "@action:button"
@@ -1932,101 +1955,103 @@ msgid ""
"Open the directory\n"
"with macro and icon"
msgstr ""
+"Verzeichnis\n"
+"mit Makro und Symbol öffnen"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:160
msgctxt "@description:label"
msgid "Instructions:"
-msgstr ""
+msgstr "Anweisungen:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:202
msgctxt "@action:playpause"
msgid "Play"
-msgstr ""
+msgstr "Wiedergeben"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:206
msgctxt "@action:playpause"
msgid "Pause"
-msgstr ""
+msgstr "Pausieren"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:268
msgctxt "@action:button"
msgid "Previous Step"
-msgstr ""
+msgstr "Vorheriger Schritt"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:283
msgctxt "@action:button"
msgid "Done"
-msgstr ""
+msgstr "Fertig"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:287
msgctxt "@action:button"
msgid "Next Step"
-msgstr ""
+msgstr "Nächster Schritt"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:21
msgctxt "@title:window"
msgid "SolidWorks plugin: Configuration"
-msgstr ""
+msgstr "SolidWorks Plugin: Konfiguration"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:39
msgctxt "@title:tab"
msgid "Conversion settings"
-msgstr ""
+msgstr "Konvertierungseinstellungen"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:66
msgctxt "@label"
msgid "First choice:"
-msgstr ""
+msgstr "Erste Wahl:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:86
msgctxt "@text:menu"
msgid "Latest installed version (Recommended)"
-msgstr ""
+msgstr "Aktuelle installierte Version (Empfohlen)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:95
msgctxt "@text:menu"
msgid "Default version"
-msgstr ""
+msgstr "Standardversion"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:193
msgctxt "@label"
msgid "Show wizard before opening SolidWorks files"
-msgstr ""
+msgstr "Assistent vor dem Öffnen von SolidWorks-Dateien anzeigen"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:203
msgctxt "@label"
msgid "Automatically rotate opened file into normed orientation"
-msgstr ""
+msgstr "Geöffnete Datei automatisch in normale Ausrichtung drehen"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:210
msgctxt "@title:tab"
msgid "Installation(s)"
-msgstr ""
+msgstr "Installation(en)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:284
msgctxt "@label"
msgid "COM service found"
-msgstr ""
+msgstr "COM-Service gefunden"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:295
msgctxt "@label"
msgid "Executable found"
-msgstr ""
+msgstr "Ausführbare Datei gefunden"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:306
msgctxt "@label"
msgid "COM starting"
-msgstr ""
+msgstr "COM wird gestartet"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:317
msgctxt "@label"
msgid "Revision number"
-msgstr ""
+msgstr "Revisionsnummer"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:328
msgctxt "@label"
msgid "Functions available"
-msgstr ""
+msgstr "Verfügbare Funktionen"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:341
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:264
@@ -2212,32 +2237,32 @@ msgstr "Glättung"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:38
msgctxt "@label"
msgid "Mesh Type"
-msgstr ""
+msgstr "Mesh-Typ"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:69
msgctxt "@label"
msgid "Normal model"
-msgstr ""
+msgstr "Normales Modell"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:76
msgctxt "@label"
msgid "Print as support"
-msgstr ""
+msgstr "Als Stützstruktur drucken"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:84
msgctxt "@label"
msgid "Don't support overlap with other models"
-msgstr ""
+msgstr "Keine Überlappung mit anderen Modellen unterstützen"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:92
msgctxt "@label"
msgid "Modify settings for overlap with other models"
-msgstr ""
+msgstr "Einstellungen für Überlappung mit anderen Modellen bearbeiten"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:100
msgctxt "@label"
msgid "Modify settings for infill of other models"
-msgstr ""
+msgstr "Einstellungen für Füllung von anderen Modellen bearbeiten"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:333
msgctxt "@action:button"
@@ -2736,7 +2761,7 @@ msgstr "Verwerfen"
#: /home/ruben/Projects/Cura/resources/qml/DiscardOrKeepProfileChangesDialog.qml:209
msgctxt "@action:button"
msgid "Keep"
-msgstr "Übernehmen"
+msgstr "Beibehalten"
#: /home/ruben/Projects/Cura/resources/qml/DiscardOrKeepProfileChangesDialog.qml:222
msgctxt "@action:button"
@@ -3097,27 +3122,27 @@ msgstr "(Anonyme) Druckinformationen senden"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:674
msgctxt "@label"
msgid "Experimental"
-msgstr ""
+msgstr "Experimentell"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:680
msgctxt "@info:tooltip"
msgid "Use multi build plate functionality"
-msgstr ""
+msgstr "Mehrfach-Druckplattenfunktion verwenden"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:685
msgctxt "@option:check"
msgid "Use multi build plate functionality (restart required)"
-msgstr ""
+msgstr "Mehrfach-Druckplattenfunktion verwenden (Neustart erforderlich)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:694
msgctxt "@info:tooltip"
msgid "Should newly loaded models be arranged on the build plate? Used in conjunction with multi build plate (EXPERIMENTAL)"
-msgstr ""
+msgstr "Sollen neu geladene Modelle auf der Druckplatte angeordnet werden? In Verbindung mit Mehrfach-Druckplatte verwenden (EXPERIMENTELL)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:699
msgctxt "@option:check"
msgid "Do not arrange objects on load"
-msgstr ""
+msgstr "Keine Objekte beim Laden anordnen"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:15
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:514
@@ -3460,6 +3485,11 @@ msgctxt "@label"
msgid "SVG icons"
msgstr "SVG-Symbole"
+#: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:139
+msgctxt "@label"
+msgid "Linux cross-distribution application deployment"
+msgstr "Distributionsunabhängiges Format für Linux-Anwendungen"
+
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:41
msgctxt "@label"
msgid "Profile:"
@@ -3530,7 +3560,7 @@ msgstr "Wird beeinflusst von"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:156
msgctxt "@label"
msgid "This setting is always shared between all extruders. Changing it here will change the value for all extruders."
-msgstr ""
+msgstr "Diese Einstellung wird stets zwischen allen Extrudern geteilt. Eine Änderung ändert den Wert für alle Extruder."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:159
msgctxt "@label"
@@ -3581,7 +3611,7 @@ msgstr "00 Stunden 00 Minuten"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:359
msgctxt "@tooltip"
msgid "Time specification"
-msgstr ""
+msgstr "Zeitangabe"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:441
msgctxt "@label"
@@ -3638,12 +3668,12 @@ msgstr "&Ansicht"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:37
msgctxt "@action:inmenu menubar:view"
msgid "&Camera position"
-msgstr ""
+msgstr "&Kameraposition"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:52
msgctxt "@action:inmenu menubar:view"
msgid "&Build plate"
-msgstr ""
+msgstr "&Druckplatte"
#: /home/ruben/Projects/Cura/resources/qml/Menus/NozzleMenu.qml:40
msgctxt "@title:menuitem %1 is the nozzle currently loaded in the printer"
@@ -3813,27 +3843,27 @@ msgstr "&Beenden"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:114
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
-msgstr ""
+msgstr "&3D-Ansicht"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:121
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
-msgstr ""
+msgstr "&Vorderansicht"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:128
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
-msgstr ""
+msgstr "&Draufsicht"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:135
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
-msgstr ""
+msgstr "&Ansicht von links"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:142
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
-msgstr ""
+msgstr "&Ansicht von rechts"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:149
msgctxt "@action:inmenu"
@@ -3959,7 +3989,7 @@ msgstr "Alle Modelle neu &laden"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:edit"
msgid "Arrange All Models To All Build Plates"
-msgstr ""
+msgstr "Alle Modelle an allen Druckplatten anordnen"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:358
msgctxt "@action:inmenu menubar:edit"
@@ -4019,7 +4049,7 @@ msgstr "Installierte plugins..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:438
msgctxt "@action:inmenu menubar:view"
msgid "Expand/Collapse Sidebar"
-msgstr ""
+msgstr "Seitenleiste vergrößern/verkleinern"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:26
msgctxt "@label:PrintjobStatus"
@@ -4054,12 +4084,12 @@ msgstr "Slicing ist nicht verfügbar"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Slice current printjob"
-msgstr ""
+msgstr "Aktuellen Druckauftrag slicen"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Cancel slicing process"
-msgstr ""
+msgstr "Slicing-Vorgang abbrechen"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:183
msgctxt "@label:Printjob"
@@ -4115,7 +4145,7 @@ msgstr "Speichern &Als"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:139
msgctxt "@title:menu menubar:file"
msgid "Save &Project..."
-msgstr ""
+msgstr "&Projekt speichern..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:162
msgctxt "@title:menu menubar:toplevel"
@@ -4354,7 +4384,7 @@ msgstr "Material"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:352
msgctxt "@label"
msgid "Check compatibility"
-msgstr ""
+msgstr "Kompatibilität prüfen"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:372
msgctxt "@tooltip"
@@ -4364,17 +4394,17 @@ msgstr "Klicken Sie, um die Materialkompatibilität auf Ultimaker.com zu prüfen
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:211
msgctxt "@option:check"
msgid "See only current build plate"
-msgstr ""
+msgstr "Nur aktuelle Druckplatte anzeigen"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:227
msgctxt "@action:button"
msgid "Arrange to all build plates"
-msgstr ""
+msgstr "An allen Druckplatten ausrichten"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:247
msgctxt "@action:button"
msgid "Arrange current build plate"
-msgstr ""
+msgstr "An aktueller Druckplatte ausrichten"
#: MachineSettingsAction/plugin.json
msgctxt "description"
@@ -4469,22 +4499,22 @@ msgstr "USB-Drucken"
#: PrepareStage/plugin.json
msgctxt "description"
msgid "Provides a prepare stage in Cura."
-msgstr ""
+msgstr "Bietet eine Vorbereitungsstufe in Cura."
#: PrepareStage/plugin.json
msgctxt "name"
msgid "Prepare Stage"
-msgstr ""
+msgstr "Vorbereitungsstufe"
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "description"
msgid "Provides an edit window for direct script editing."
-msgstr ""
+msgstr "Bietet ein Bearbeitungsfenster für direkte Skriptbearbeitung."
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "name"
msgid "Live scripting tool"
-msgstr ""
+msgstr "Live-Scripting-Tool"
#: RemovableDriveOutputDevice/plugin.json
msgctxt "description"
@@ -4509,12 +4539,12 @@ msgstr "UM3-Netzwerkverbindung"
#: MonitorStage/plugin.json
msgctxt "description"
msgid "Provides a monitor stage in Cura."
-msgstr ""
+msgstr "Bietet eine Überwachungsstufe in Cura."
#: MonitorStage/plugin.json
msgctxt "name"
msgid "Monitor Stage"
-msgstr ""
+msgstr "Überwachungsstufe"
#: FirmwareUpdateChecker/plugin.json
msgctxt "description"
@@ -4529,7 +4559,7 @@ msgstr "Firmware-Update-Prüfer"
#: CuraSolidWorksPlugin/plugin.json
msgctxt "description"
msgid "Gives you the possibility to open certain files using SolidWorks itself. Conversion is done by this plugin and additional optimizations."
-msgstr ""
+msgstr "Bietet Ihnen die Möglichkeit, bestimmte Dateien über SolidWorks selbst zu öffnen. Die Konvertierung erfolgt über dieses Plugin und zusätzliche Optimierungen."
#: CuraSolidWorksPlugin/plugin.json
msgctxt "name"
@@ -4599,12 +4629,12 @@ msgstr "Cura-Vorgängerprofil-Reader"
#: CuraBlenderPlugin/plugin.json
msgctxt "description"
msgid "Helps to open Blender files directly in Cura."
-msgstr ""
+msgstr "Unterstützt das Öffnen der Blender-Dateien direkt in Cura."
#: CuraBlenderPlugin/plugin.json
msgctxt "name"
msgid "Blender Integration (experimental)"
-msgstr ""
+msgstr "Blender-Integration (experimentell)"
#: GCodeProfileReader/plugin.json
msgctxt "description"
@@ -4769,12 +4799,12 @@ msgstr "Cura-Profil-Writer"
#: CuraPrintProfileCreator/plugin.json
msgctxt "description"
msgid "Allows material manufacturers to create new material and quality profiles using a drop-in UI."
-msgstr ""
+msgstr "Ermöglichen Sie Materialherstellern die Erstellung neuer Material- und Qualitätsprofile, indem Sie eine Drop-In-Benutzerschnittstelle verwenden."
#: CuraPrintProfileCreator/plugin.json
msgctxt "name"
msgid "Print Profile Assistant"
-msgstr ""
+msgstr "Druckprofil-Assistent"
#: 3MFWriter/plugin.json
msgctxt "description"
diff --git a/resources/i18n/de_DE/fdmextruder.def.json.po b/resources/i18n/de_DE/fdmextruder.def.json.po
index 039cc5dcb6..4c81c54c35 100644
--- a/resources/i18n/de_DE/fdmextruder.def.json.po
+++ b/resources/i18n/de_DE/fdmextruder.def.json.po
@@ -1,11 +1,11 @@
-# Cura JSON setting files
-# Copyright (C) 2017 Ultimaker
+# Cura
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
"POT-Creation-Date: 2017-08-02 16:53+0000\n"
"PO-Revision-Date: 2017-11-30 13:05+0100\n"
diff --git a/resources/i18n/de_DE/fdmprinter.def.json.po b/resources/i18n/de_DE/fdmprinter.def.json.po
index c70b25d7a8..95422bb589 100644
--- a/resources/i18n/de_DE/fdmprinter.def.json.po
+++ b/resources/i18n/de_DE/fdmprinter.def.json.po
@@ -1,13 +1,13 @@
-# Cura JSON setting files
-# Copyright (C) 2017 Ultimaker
+# Cura
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
"PO-Revision-Date: 2017-11-30 13:05+0100\n"
"Last-Translator: Bothof \n"
"Language-Team: German\n"
@@ -56,9 +56,7 @@ msgctxt "machine_start_gcode description"
msgid ""
"Gcode commands to be executed at the very start - separated by \n"
"."
-msgstr ""
-"Gcode-Befehle, die zu Beginn ausgeführt werden sollen – getrennt durch \n"
-"."
+msgstr "Gcode-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 ""
"Gcode commands to be executed at the very end - separated by \n"
"."
-msgstr ""
-"Gcode-Befehle, die Am Ende ausgeführt werden sollen – getrennt durch \n"
-"."
+msgstr "Gcode-Befehle, die Am Ende ausgeführt werden sollen – getrennt durch \n."
#: fdmprinter.def.json
msgctxt "material_guid label"
@@ -352,12 +348,12 @@ msgstr "Repetier"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract label"
msgid "Firmware Retraction"
-msgstr ""
+msgstr "Firmware-Einzug"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract description"
msgid "Whether to use firmware retract commands (G10/G11) instead of using the E property in G1 commands to retract the material."
-msgstr ""
+msgstr "Definiert, ob Firmware-Einzugsbefehle (G10/G11) anstelle der E-Eigenschaft in G1-Befehlen verwendet wird, um das Material einzuziehen."
#: fdmprinter.def.json
msgctxt "machine_disallowed_areas label"
@@ -1052,12 +1048,12 @@ msgstr "Überall"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps label"
msgid "Filter Out Tiny Gaps"
-msgstr ""
+msgstr "Kleine Lücken ausfiltern"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps description"
msgid "Filter out tiny gaps to reduce blobs on outside of model."
-msgstr ""
+msgstr "Kleine Lücken ausfiltern, um Tropfen an der Außenseite des Modells zu reduzieren."
#: fdmprinter.def.json
msgctxt "fill_outline_gaps label"
@@ -1442,7 +1438,7 @@ msgstr "X-Versatz Füllung"
#: fdmprinter.def.json
msgctxt "infill_offset_x description"
msgid "The infill pattern is moved this distance along the X axis."
-msgstr ""
+msgstr "Das Füllmuster wird um diese Distanz entlang der X-Achse verschoben."
#: fdmprinter.def.json
msgctxt "infill_offset_y label"
@@ -1452,7 +1448,7 @@ msgstr "Y-Versatz Füllung"
#: fdmprinter.def.json
msgctxt "infill_offset_y description"
msgid "The infill pattern is moved this distance along the Y axis."
-msgstr ""
+msgstr "Das Füllmuster wird um diese Distanz entlang der Y-Achse verschoben."
#: fdmprinter.def.json
msgctxt "sub_div_rad_add label"
@@ -1472,7 +1468,7 @@ msgstr "Prozentsatz Füllung überlappen"
#: fdmprinter.def.json
msgctxt "infill_overlap description"
msgid "The amount of overlap between the infill and the walls as a percentage of the infill line width. A slight overlap allows the walls to connect firmly to the infill."
-msgstr ""
+msgstr "Das Ausmaß des Überlappens zwischen der Füllung und den Wänden als Prozentwert der Füllungslinienbreite. Ein leichtes Überlappen ermöglicht es den Wänden, eine solide Verbindung mit der Füllung herzustellen."
#: fdmprinter.def.json
msgctxt "infill_overlap_mm label"
@@ -1492,7 +1488,7 @@ msgstr "Prozentsatz Außenhaut überlappen"
#: fdmprinter.def.json
msgctxt "skin_overlap description"
msgid "The amount of overlap between the skin and the walls as a percentage of the skin line width. A slight overlap allows the walls to connect firmly to the skin. This is a percentage of the average line widths of the skin lines and the innermost wall."
-msgstr ""
+msgstr "Das Ausmaß des Überlappens zwischen der Außenhaut und den Wänden als Prozentwert der Außenhaut-Linienbreite. Ein leichtes Überlappen ermöglicht es den Wänden, eine solide Verbindung mit der Außenhaut herzustellen. Dies ist ein Prozentwert der durchschnittlichen Linienbreiten der Außenhautlinien und der innersten Wand."
#: fdmprinter.def.json
msgctxt "skin_overlap_mm label"
@@ -1722,7 +1718,7 @@ msgstr "Temperatur Druckplatte"
#: fdmprinter.def.json
msgctxt "material_bed_temperature description"
msgid "The temperature used for the heated build plate. If this is 0, the bed temperature will not be adjusted."
-msgstr ""
+msgstr "Die Temperatur, die für die erhitzte Druckplatte verwendet wird. Wenn dieser Wert 0 beträgt, wird die Betttemperatur nicht angepasst."
#: fdmprinter.def.json
msgctxt "material_bed_temperature_layer_0 label"
@@ -3504,9 +3500,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"
@@ -4266,82 +4260,82 @@ msgstr "experimentell!"
#: fdmprinter.def.json
msgctxt "support_tree_enable label"
msgid "Tree Support"
-msgstr ""
+msgstr "Baumstruktur"
#: fdmprinter.def.json
msgctxt "support_tree_enable description"
msgid "Generate a tree-like support with branches that support your print. This may reduce material usage and print time, but greatly increases slicing time."
-msgstr ""
+msgstr "Erstellen Sie eine baumähnliche Stützstruktur mit Ästen, die Ihren Druck stützen. Das reduziert möglicherweise den Materialverbrauch und die Druckdauer, erhöht jedoch die Slicing-Dauer erheblich."
#: fdmprinter.def.json
msgctxt "support_tree_angle label"
msgid "Tree Support Branch Angle"
-msgstr ""
+msgstr "Astwinkel der Baumstützstruktur"
#: fdmprinter.def.json
msgctxt "support_tree_angle description"
msgid "The angle of the branches. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach."
-msgstr ""
+msgstr "Dies bezeichnet den Winkel der Äste. Verwenden Sie einen geringeren Winkel, um sie vertikaler und stabiler zu gestalten. Verwenden Sie einen stärkeren Winkel, um mehr Reichweite zu erhalten."
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance label"
msgid "Tree Support Branch Distance"
-msgstr ""
+msgstr "Astabstand der Baumstützstruktur"
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance description"
msgid "How far apart the branches need to be when they touch the model. Making this distance small will cause the tree support to touch the model at more points, causing better overhang but making support harder to remove."
-msgstr ""
+msgstr "Dies beschreibt, wie weit die Äste weg sein müssen, wenn sie das Modell berühren. Eine geringe Entfernung lässt die Baumstützstruktur das Modell an mehreren Punkten berühren, und führt zu einem besseren Überhang, allerdings lässt sich die Stützstruktur auch schwieriger entfernen."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter label"
msgid "Tree Support Branch Diameter"
-msgstr ""
+msgstr "Astdurchmesser der Baumstützstruktur"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter description"
msgid "The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this."
-msgstr ""
+msgstr "Dies beschreibt den Durchmesser der dünnsten Äste der Baumstützstruktur. Dickere Äste sind stabiler. Äste zur Basis hin werden dicker als diese sein."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle label"
msgid "Tree Support Branch Diameter Angle"
-msgstr ""
+msgstr "Winkel Astdurchmesser der Baumstützstruktur"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle description"
msgid "The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the tree support."
-msgstr ""
+msgstr "Dies beschreibt den Winkel der Astdurchmesser, da sie stufenweise zum Boden hin dicker werden. Ein Winkel von 0 lässt die Äste über die gesamte Länge hinweg eine gleiche Dicke haben. Ein geringer Winkel kann die Stabilität der Baumstützstruktur erhöhen."
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution label"
msgid "Tree Support Collision Resolution"
-msgstr ""
+msgstr "Kollisionsauflösung der Baumstützstruktur"
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution description"
msgid "Resolution to compute collisions with to avoid hitting the model. Setting this lower will produce more accurate trees that fail less often, but increases slicing time dramatically."
-msgstr ""
+msgstr "Dies ist die Auflösung für die Berechnung von Kollisionen, um ein Anschlagen des Modells zu verhindern. Eine niedrigere Einstellung sorgt für akkuratere Bäume, die weniger häufig fehlschlagen, erhöht jedoch die Slicing-Zeit erheblich."
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness label"
msgid "Tree Support Wall Thickness"
-msgstr ""
+msgstr "Wanddicke der Baumstützstruktur"
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness description"
msgid "The thickness of the walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "Das ist die Dicke der Astwände der Baumstützstruktur. Dickere Wände benötigen eine längere Druckdauer, fallen jedoch nicht so leicht um."
#: fdmprinter.def.json
msgctxt "support_tree_wall_count label"
msgid "Tree Support Wall Line Count"
-msgstr ""
+msgstr "Anzahl der Wandlinien der Baumstützstruktur"
#: fdmprinter.def.json
msgctxt "support_tree_wall_count description"
msgid "The number of walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "Das ist die Anzahl der Astwände der Baumstützstruktur. Dickere Wände benötigen eine längere Druckdauer, fallen jedoch nicht so leicht um."
#: fdmprinter.def.json
msgctxt "slicing_tolerance label"
@@ -4416,12 +4410,12 @@ msgstr "Eine Liste von Ganzzahl-Linienrichtungen für den Fall, wenn die oberen
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization label"
msgid "Infill Travel Optimization"
-msgstr ""
+msgstr "Bewegungsoptimierung Füllung"
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization description"
msgid "When enabled, the order in which the infill lines are printed is optimized to reduce the distance travelled. The reduction in travel time achieved very much depends on the model being sliced, infill pattern, density, etc. Note that, for some models that have many small areas of infill, the time to slice the model may be greatly increased."
-msgstr ""
+msgstr "Bei Aktivierung wird die Reihenfolge, in der die Fülllinien gedruckt werden, optimiert, um die gefahrene Distanz zu reduzieren. Diese erzielte Reduzierung der Bewegung ist sehr stark von dem zu slicenden Modell, dem Füllmuster, der Dichte usw. abhängig. Beachten Sie, dass die Dauer für das Slicen bei einigen Modellen mit vielen kleinen Füllbereichen erheblich länger ausfallen kann."
#: fdmprinter.def.json
msgctxt "material_flow_dependent_temperature label"
@@ -4943,9 +4937,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"
@@ -5055,42 +5047,42 @@ msgstr "Der Abstand zwischen der Düse und den horizontalen Abwärtslinien. Bei
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled label"
msgid "Use adaptive layers"
-msgstr ""
+msgstr "Anpassschichten verwenden"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled description"
msgid "Adaptive layers computes the layer heights depending on the shape of the model."
-msgstr ""
+msgstr "Die Funktion Anpassschichten berechnet die Schichthöhe je nach Form des Modells."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation label"
msgid "Adaptive layers maximum variation"
-msgstr ""
+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 in mm."
-msgstr ""
+msgstr "Das ist die maximal zulässige Höhendifferenz von der Basisschichthöhe in mm."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step label"
msgid "Adaptive layers variation step size"
-msgstr ""
+msgstr "Abweichung Schrittgröße für Anpassschichten"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step description"
msgid "The difference in height of the next layer height compared to the previous one."
-msgstr ""
+msgstr "Der Höhenunterscheid der nächsten Schichthöhe im Vergleich zur vorherigen."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold label"
msgid "Adaptive layers threshold"
-msgstr ""
+msgstr "Schwellenwert Anpassschichten"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold description"
msgid "Threshold whether to use a smaller layer or not. This number is compared to the tan of the steepest slope in a layer."
-msgstr ""
+msgstr "Das ist der Schwellenwert, der definiert, ob eine kleinere Schicht verwendet wird oder nicht. Dieser Wert wird mit dem der stärksten Neigung in einer Schicht verglichen."
#: fdmprinter.def.json
msgctxt "command_line_settings label"
diff --git a/resources/i18n/es_ES/cura.po b/resources/i18n/es_ES/cura.po
index debb9dbfca..fa8cf61985 100644
--- a/resources/i18n/es_ES/cura.po
+++ b/resources/i18n/es_ES/cura.po
@@ -1,20 +1,22 @@
# Cura
-# Copyright (C) 2017 Ultimaker
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
-"PO-Revision-Date: 2017-11-30 13:05+0100\n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
+"PO-Revision-Date: 2018-02-12 13:40+0100\n"
"Last-Translator: Bothof \n"
"Language-Team: Spanish\n"
"Language: es_ES\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.0.6\n"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:26
msgctxt "@action"
@@ -187,7 +189,7 @@ msgstr "Firmware de la impresora"
#: /home/ruben/Projects/Cura/plugins/PrepareStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Prepare"
-msgstr ""
+msgstr "Preparar"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:23
msgctxt "@action:button Preceded by 'Ready to'."
@@ -560,7 +562,7 @@ msgstr "Abre la interfaz de trabajos de impresión en el navegador."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:239
msgctxt "@label Printer name"
msgid "Unknown"
-msgstr ""
+msgstr "Desconocido"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:505
#, python-brace-format
@@ -595,7 +597,7 @@ msgstr "Conectar a través de la red"
#: /home/ruben/Projects/Cura/plugins/MonitorStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Monitor"
-msgstr ""
+msgstr "Supervisar"
#: /home/ruben/Projects/Cura/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py:66
#, python-brace-format
@@ -622,7 +624,7 @@ msgstr "No se pudo acceder a la información actualizada."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:579
msgctxt "@info:status"
msgid "SolidWorks reported errors, while opening your file. We recommend to solve these issues inside SolidWorks itself."
-msgstr ""
+msgstr "SolidWorks ha informado de errores al abrir el archivo. Le recomendamos que solucione estos problemas dentro del propio SolidWorks."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:591
msgctxt "@info:status"
@@ -631,6 +633,9 @@ msgid ""
"\n"
" Thanks!."
msgstr ""
+"No se han encontrado modelos en el dibujo. ¿Puede comprobar el contenido de nuevo y asegurarse de que hay una parte o un ensamblado dentro?\n"
+"\n"
+" Gracias."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:595
msgctxt "@info:status"
@@ -639,6 +644,9 @@ msgid ""
"\n"
"Sorry!"
msgstr ""
+"Se ha encontrado más de una parte o ensamblado en el dibujo. Actualmente únicamente son compatibles dibujos con una sola parte o ensamblado.\n"
+"\n"
+" Disculpe."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:25
msgctxt "@item:inlistbox"
@@ -653,7 +661,7 @@ msgstr "Archivo de ensamblado de SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:33
msgctxt "@item:inlistbox"
msgid "SolidWorks drawing file"
-msgstr ""
+msgstr "Archivo de dibujo de SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:48
msgctxt "@info:status"
@@ -664,6 +672,11 @@ msgid ""
"With kind regards\n"
" - Thomas Karl Pietrowski"
msgstr ""
+"Estimado cliente:\n"
+"No hemos encontrado una instalación válida de SolidWorks en el sistema. Esto significa que SolidWorks no está instalado o que no dispone de una licencia válida. Asegúrese de que la ejecución del propio SolidWorks funciona sin problemas o póngase en contacto con su CDTI.\n"
+"\n"
+"Atentamente\n"
+" - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:57
msgctxt "@info:status"
@@ -674,6 +687,11 @@ msgid ""
"With kind regards\n"
" - Thomas Karl Pietrowski"
msgstr ""
+"Estimado cliente:\n"
+"Actualmente está ejecutando este complemento en un sistema operativo diferente a Windows. Este complemento solo funcionará en Windows con SolidWorks instalado, siempre que se disponga de una licencia válida. Instale este complemento en un equipo Windows con SolidWorks instalado.\n"
+"\n"
+"Atentamente\n"
+" - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:70
msgid "Configure"
@@ -681,7 +699,7 @@ msgstr "Configurar"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:71
msgid "Installation guide for SolidWorks macro"
-msgstr ""
+msgstr "Guía de instalación para la macro de SolidWorks"
#: /home/ruben/Projects/Cura/plugins/SimulationView/__init__.py:14
msgctxt "@item:inlistbox"
@@ -705,7 +723,7 @@ msgstr "Modificar GCode"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:43
msgctxt "@info"
msgid "Cura collects anonymized usage statistics."
-msgstr ""
+msgstr "Cura recopila estadísticas de uso de forma anónima."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:46
msgctxt "@info:title"
@@ -715,22 +733,22 @@ msgstr "Recopilando datos"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:48
msgctxt "@action:button"
msgid "Allow"
-msgstr ""
+msgstr "Permitir"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:49
msgctxt "@action:tooltip"
msgid "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing."
-msgstr ""
+msgstr "Permitir a Cura enviar estadísticas de uso de forma anónima para ayudar a priorizar mejoras futuras para Cura. Se envían algunas de sus preferencias y ajustes, la versión de Cura y un resumen de los modelos que está fragmentando."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:50
msgctxt "@action:button"
msgid "Disable"
-msgstr ""
+msgstr "Deshabilitar"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:51
msgctxt "@action:tooltip"
msgid "Don't allow Cura to send anonymized usage statistics. You can enable it again in the preferences."
-msgstr ""
+msgstr "No permitir a Cura enviar estadísticas de uso de forma anónima. Puede habilitarlo de nuevo en las preferencias."
#: /home/ruben/Projects/Cura/plugins/LegacyProfileReader/__init__.py:14
msgctxt "@item:inlistbox"
@@ -740,7 +758,7 @@ msgstr "Perfiles de Cura 15.04"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/__init__.py:15
msgctxt "@item:inlistbox"
msgid "Blender file"
-msgstr ""
+msgstr "Archivo Blender"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/CadIntegrationUtils/CommonReader.py:199
msgctxt "@info:status"
@@ -748,6 +766,8 @@ msgid ""
"Could not export using \"{}\" quality!\n"
"Felt back to \"{}\"."
msgstr ""
+"No ha podido exportarse con la calidad \"{}\"\n"
+"Retroceder a \"{}»."
#: /home/ruben/Projects/Cura/plugins/GCodeProfileReader/__init__.py:14
#: /home/ruben/Projects/Cura/plugins/GCodeReader/__init__.py:14
@@ -934,12 +954,12 @@ msgstr "Perfil de cura"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:12
msgctxt "@item:inmenu"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Asistente del perfil"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:17
msgctxt "@item:inlistbox"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Asistente del perfil"
#: /home/ruben/Projects/Cura/plugins/3MFWriter/__init__.py:30
msgctxt "@item:inlistbox"
@@ -995,12 +1015,12 @@ msgstr "Relleno"
#: /home/ruben/Projects/Cura/cura/PrintInformation.py:102
msgctxt "@tooltip"
msgid "Support Infill"
-msgstr "Relleno de soporte"
+msgstr "Relleno del soporte"
#: /home/ruben/Projects/Cura/cura/PrintInformation.py:103
msgctxt "@tooltip"
msgid "Support Interface"
-msgstr "Interfaz de soporte"
+msgstr "Interfaz del soporte"
#: /home/ruben/Projects/Cura/cura/PrintInformation.py:104
msgctxt "@tooltip"
@@ -1137,13 +1157,13 @@ msgstr "Error al importar el perfil de {0}: {1} or !"
msgid "This profile {0} contains incorrect data, could not import it."
-msgstr ""
+msgstr "Este perfil {0} contiene datos incorrectos, no se han podido importar."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:240
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "The machine defined in profile {0} doesn't match with your current machine, could not import it."
-msgstr ""
+msgstr "El equipo definido en el perfil {0} no coincide con su equipo actual, no se ha podido importar."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:274
#, python-brace-format
@@ -1155,7 +1175,7 @@ msgstr "Perfil {0} importado correctamente"
#, python-brace-format
msgctxt "@info:status"
msgid "File {0} does not contain any valid profile."
-msgstr ""
+msgstr "El archivo {0} no contiene ningún perfil válido."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:280
#, python-brace-format
@@ -1183,7 +1203,7 @@ msgstr "No se ha podido encontrar un tipo de calidad {0} para la configuración
#, python-brace-format
msgctxt "@label"
msgid "Group #{group_nr}"
-msgstr ""
+msgstr "N.º de grupo {group_nr}"
#: /home/ruben/Projects/Cura/cura/BuildVolume.py:100
msgctxt "@info:status"
@@ -1243,6 +1263,9 @@ msgid ""
"
Please use the \"Send report\" button to post a bug report automatically to our servers
\n"
" "
msgstr ""
+"
Se ha producido un error grave. Envíenos este informe de incidencias para que podamos solucionar el problema.
\n"
+"
Utilice el botón «Enviar informe» para publicar automáticamente un informe de errores en nuestros servidores.
"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:147
msgctxt "@title:groupbox"
msgid "Error traceback"
-msgstr ""
+msgstr "Rastreabilidad de errores"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:214
msgctxt "@title:groupbox"
@@ -1516,7 +1539,7 @@ msgstr "Tamaño de la tobera"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:393
msgctxt "@label"
msgid "Compatible material diameter"
-msgstr ""
+msgstr "Diámetro del material compatible"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:395
msgctxt "@tooltip"
@@ -1661,12 +1684,12 @@ msgstr "Tipo"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:233
msgctxt "@label Printer name"
msgid "Ultimaker 3"
-msgstr ""
+msgstr "Ultimaker 3"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:236
msgctxt "@label Printer name"
msgid "Ultimaker 3 Extended"
-msgstr ""
+msgstr "Ultimaker 3 Extended"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:252
msgctxt "@label"
@@ -1733,7 +1756,7 @@ msgstr "%1 no está configurada para alojar un grupo de impresoras conectadas de
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml:55
msgctxt "@label link to connect manager"
msgid "Add/Remove printers"
-msgstr ""
+msgstr "Agregar/eliminar impresoras"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/OpenPanelButton.qml:14
msgctxt "@info:tooltip"
@@ -1773,7 +1796,7 @@ msgstr "Se ha perdido la conexión con la impresora."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:47
msgctxt "@label Printer status"
msgid "Unknown"
-msgstr ""
+msgstr "Desconocido"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:257
msgctxt "@label:status"
@@ -1869,62 +1892,62 @@ msgstr "Activar configuración"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:21
msgctxt "@title:window"
msgid "SolidWorks: Export wizard"
-msgstr ""
+msgstr "SolidWorks: exportar asistente"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:45
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:140
msgctxt "@action:label"
msgid "Quality:"
-msgstr ""
+msgstr "Calidad:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:78
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:179
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (3D-printing)"
-msgstr ""
+msgstr "Fina (impresión en 3D)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:79
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:180
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (3D-printing)"
-msgstr ""
+msgstr "Gruesa (impresión en 3D)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:80
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:181
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (SolidWorks)"
-msgstr ""
+msgstr "Fina (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:81
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:182
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (SolidWorks)"
-msgstr ""
+msgstr "Gruesa (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:94
msgctxt "@text:window"
msgid "Show this dialog again"
-msgstr ""
+msgstr "Mostrar este cuadro de diálogo de nuevo"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:104
msgctxt "@action:button"
msgid "Continue"
-msgstr ""
+msgstr "Continuar"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:116
msgctxt "@action:button"
msgid "Abort"
-msgstr ""
+msgstr "Cancelar"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:21
msgctxt "@title:window"
msgid "How to install Cura SolidWorks macro"
-msgstr ""
+msgstr "Cómo instalar la macro SolidWorks de Cura"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:62
msgctxt "@description:label"
msgid "Steps:"
-msgstr ""
+msgstr "Pasos:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:140
msgctxt "@action:button"
@@ -1932,101 +1955,103 @@ msgid ""
"Open the directory\n"
"with macro and icon"
msgstr ""
+"Abra el directorio\n"
+"con la macro y el icono"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:160
msgctxt "@description:label"
msgid "Instructions:"
-msgstr ""
+msgstr "Instrucciones:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:202
msgctxt "@action:playpause"
msgid "Play"
-msgstr ""
+msgstr "Reproducir"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:206
msgctxt "@action:playpause"
msgid "Pause"
-msgstr ""
+msgstr "Pausar"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:268
msgctxt "@action:button"
msgid "Previous Step"
-msgstr ""
+msgstr "Paso anterior"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:283
msgctxt "@action:button"
msgid "Done"
-msgstr ""
+msgstr "Realizado"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:287
msgctxt "@action:button"
msgid "Next Step"
-msgstr ""
+msgstr "Paso siguiente"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:21
msgctxt "@title:window"
msgid "SolidWorks plugin: Configuration"
-msgstr ""
+msgstr "Complementos de SolidWorks: configuración"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:39
msgctxt "@title:tab"
msgid "Conversion settings"
-msgstr ""
+msgstr "Ajustes de la conversión"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:66
msgctxt "@label"
msgid "First choice:"
-msgstr ""
+msgstr "Primera opción:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:86
msgctxt "@text:menu"
msgid "Latest installed version (Recommended)"
-msgstr ""
+msgstr "Última versión instalada (recomendada)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:95
msgctxt "@text:menu"
msgid "Default version"
-msgstr ""
+msgstr "Versión predeterminada"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:193
msgctxt "@label"
msgid "Show wizard before opening SolidWorks files"
-msgstr ""
+msgstr "Mostrar asistente antes de abrir los archivos de SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:203
msgctxt "@label"
msgid "Automatically rotate opened file into normed orientation"
-msgstr ""
+msgstr "Girar automáticamente el archivo abierto a la orientación normal"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:210
msgctxt "@title:tab"
msgid "Installation(s)"
-msgstr ""
+msgstr "Instalación(es)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:284
msgctxt "@label"
msgid "COM service found"
-msgstr ""
+msgstr "Servicio COM encontrado"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:295
msgctxt "@label"
msgid "Executable found"
-msgstr ""
+msgstr "Ejecutable encontrado"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:306
msgctxt "@label"
msgid "COM starting"
-msgstr ""
+msgstr "Iniciando COM"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:317
msgctxt "@label"
msgid "Revision number"
-msgstr ""
+msgstr "Número de revisión"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:328
msgctxt "@label"
msgid "Functions available"
-msgstr ""
+msgstr "Funciones disponibles"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:341
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:264
@@ -2212,32 +2237,32 @@ msgstr "Suavizado"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:38
msgctxt "@label"
msgid "Mesh Type"
-msgstr ""
+msgstr "Tipo de malla"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:69
msgctxt "@label"
msgid "Normal model"
-msgstr ""
+msgstr "Modelo normal"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:76
msgctxt "@label"
msgid "Print as support"
-msgstr ""
+msgstr "Imprimir según compatibilidad"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:84
msgctxt "@label"
msgid "Don't support overlap with other models"
-msgstr ""
+msgstr "No es compatible la superposición con otros modelos"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:92
msgctxt "@label"
msgid "Modify settings for overlap with other models"
-msgstr ""
+msgstr "Modificar ajustes para superponer con otros modelos"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:100
msgctxt "@label"
msgid "Modify settings for infill of other models"
-msgstr ""
+msgstr "Modificar ajustes para rellenar con otros modelos"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:333
msgctxt "@action:button"
@@ -2791,7 +2816,7 @@ msgstr "Coste del filamento"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MaterialView.qml:203
msgctxt "@label"
msgid "Filament weight"
-msgstr "Anchura del filamento"
+msgstr "Peso del filamento"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MaterialView.qml:220
msgctxt "@label"
@@ -3097,27 +3122,27 @@ msgstr "Enviar información (anónima) de impresión"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:674
msgctxt "@label"
msgid "Experimental"
-msgstr ""
+msgstr "Experimental"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:680
msgctxt "@info:tooltip"
msgid "Use multi build plate functionality"
-msgstr ""
+msgstr "Utilizar funcionalidad de placa de impresión múltiple"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:685
msgctxt "@option:check"
msgid "Use multi build plate functionality (restart required)"
-msgstr ""
+msgstr "Utilizar funcionalidad de placa de impresión múltiple (reinicio requerido)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:694
msgctxt "@info:tooltip"
msgid "Should newly loaded models be arranged on the build plate? Used in conjunction with multi build plate (EXPERIMENTAL)"
-msgstr ""
+msgstr "¿Los modelos recién cargados se deben organizar en la placa de impresión? Se han usado junto con la placa de impresión múltiple (EXPERIMENTAL)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:699
msgctxt "@option:check"
msgid "Do not arrange objects on load"
-msgstr ""
+msgstr "No organizar objetos al cargar"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:15
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:514
@@ -3530,7 +3555,7 @@ msgstr "Afectado por"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:156
msgctxt "@label"
msgid "This setting is always shared between all extruders. Changing it here will change the value for all extruders."
-msgstr ""
+msgstr "Este ajuste siempre se comparte entre extrusores. Si lo modifica, modificará el valor de todos los extrusores."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:159
msgctxt "@label"
@@ -3581,7 +3606,7 @@ msgstr "00 h 00 min"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:359
msgctxt "@tooltip"
msgid "Time specification"
-msgstr ""
+msgstr "Especificación de tiempos"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:441
msgctxt "@label"
@@ -3638,12 +3663,12 @@ msgstr "&Ver"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:37
msgctxt "@action:inmenu menubar:view"
msgid "&Camera position"
-msgstr ""
+msgstr "&Posición de la cámara"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:52
msgctxt "@action:inmenu menubar:view"
msgid "&Build plate"
-msgstr ""
+msgstr "&Placa de impresión"
#: /home/ruben/Projects/Cura/resources/qml/Menus/NozzleMenu.qml:40
msgctxt "@title:menuitem %1 is the nozzle currently loaded in the printer"
@@ -3813,27 +3838,27 @@ msgstr "&Salir"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:114
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
-msgstr ""
+msgstr "&Vista en 3D"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:121
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
-msgstr ""
+msgstr "&Vista frontal"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:128
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
-msgstr ""
+msgstr "&Vista superior"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:135
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
-msgstr ""
+msgstr "&Vista del lado izquierdo"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:142
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
-msgstr ""
+msgstr "&Vista del lado derecho"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:149
msgctxt "@action:inmenu"
@@ -3848,7 +3873,7 @@ msgstr "&Agregar impresora..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:162
msgctxt "@action:inmenu menubar:printer"
msgid "Manage Pr&inters..."
-msgstr "Adm&inistrar impresoras ..."
+msgstr "Adm&inistrar impresoras..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:169
msgctxt "@action:inmenu"
@@ -3959,7 +3984,7 @@ msgstr "&Recargar todos los modelos"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:edit"
msgid "Arrange All Models To All Build Plates"
-msgstr ""
+msgstr "Organizar todos los modelos en todas las placas de impresión"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:358
msgctxt "@action:inmenu menubar:edit"
@@ -4019,7 +4044,7 @@ msgstr "Complementos instalados..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:438
msgctxt "@action:inmenu menubar:view"
msgid "Expand/Collapse Sidebar"
-msgstr ""
+msgstr "Expandir/contraer barra lateral"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:26
msgctxt "@label:PrintjobStatus"
@@ -4044,22 +4069,22 @@ msgstr "Listo para %1"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:42
msgctxt "@label:PrintjobStatus"
msgid "Unable to Slice"
-msgstr "No se puede segmentar."
+msgstr "No se puede segmentar"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:44
msgctxt "@label:PrintjobStatus"
msgid "Slicing unavailable"
-msgstr "No se puede segmentar"
+msgstr "Segmentación no disponible"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Slice current printjob"
-msgstr ""
+msgstr "Fragmentar trabajo de impresión actual"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Cancel slicing process"
-msgstr ""
+msgstr "Cancelar proceso de fragmentación"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:183
msgctxt "@label:Printjob"
@@ -4115,7 +4140,7 @@ msgstr "Guardar &como..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:139
msgctxt "@title:menu menubar:file"
msgid "Save &Project..."
-msgstr ""
+msgstr "Guardar &proyecto..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:162
msgctxt "@title:menu menubar:toplevel"
@@ -4354,7 +4379,7 @@ msgstr "Material"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:352
msgctxt "@label"
msgid "Check compatibility"
-msgstr ""
+msgstr "Comprobar compatibilidad"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:372
msgctxt "@tooltip"
@@ -4364,17 +4389,17 @@ msgstr "Haga clic para comprobar la compatibilidad de los materiales en Utimaker
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:211
msgctxt "@option:check"
msgid "See only current build plate"
-msgstr ""
+msgstr "Ver solo placa de impresión actual"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:227
msgctxt "@action:button"
msgid "Arrange to all build plates"
-msgstr ""
+msgstr "Organizar todas las placas de impresión"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:247
msgctxt "@action:button"
msgid "Arrange current build plate"
-msgstr ""
+msgstr "Organizar placa de impresión actual"
#: MachineSettingsAction/plugin.json
msgctxt "description"
@@ -4469,22 +4494,22 @@ msgstr "Impresión USB"
#: PrepareStage/plugin.json
msgctxt "description"
msgid "Provides a prepare stage in Cura."
-msgstr ""
+msgstr "Proporciona una fase de preparación en Cura."
#: PrepareStage/plugin.json
msgctxt "name"
msgid "Prepare Stage"
-msgstr ""
+msgstr "Fase de preparación"
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "description"
msgid "Provides an edit window for direct script editing."
-msgstr ""
+msgstr "Proporciona una ventana de edición para la edición directa de secuencias de comandos."
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "name"
msgid "Live scripting tool"
-msgstr ""
+msgstr "Herramienta de secuencia de comandos en directo"
#: RemovableDriveOutputDevice/plugin.json
msgctxt "description"
@@ -4509,12 +4534,12 @@ msgstr "Conexión de red UM3"
#: MonitorStage/plugin.json
msgctxt "description"
msgid "Provides a monitor stage in Cura."
-msgstr ""
+msgstr "Proporciona una fase de supervisión en Cura."
#: MonitorStage/plugin.json
msgctxt "name"
msgid "Monitor Stage"
-msgstr ""
+msgstr "Fase de supervisión"
#: FirmwareUpdateChecker/plugin.json
msgctxt "description"
@@ -4529,7 +4554,7 @@ msgstr "Buscador de actualizaciones de firmware"
#: CuraSolidWorksPlugin/plugin.json
msgctxt "description"
msgid "Gives you the possibility to open certain files using SolidWorks itself. Conversion is done by this plugin and additional optimizations."
-msgstr ""
+msgstr "Permite abrir determinados archivos con el propio SolidWorks. La conversión se lleva a cabo mediante este complemento y optimizaciones adicionales."
#: CuraSolidWorksPlugin/plugin.json
msgctxt "name"
@@ -4599,12 +4624,12 @@ msgstr "Lector de perfiles antiguos de Cura"
#: CuraBlenderPlugin/plugin.json
msgctxt "description"
msgid "Helps to open Blender files directly in Cura."
-msgstr ""
+msgstr "Ayuda a abrir archivos de Blender directamente en Cura."
#: CuraBlenderPlugin/plugin.json
msgctxt "name"
msgid "Blender Integration (experimental)"
-msgstr ""
+msgstr "Integración de Blender (experimental)"
#: GCodeProfileReader/plugin.json
msgctxt "description"
@@ -4709,7 +4734,7 @@ msgstr "Herramienta de ajustes por modelo"
#: cura-siemensnx-plugin/plugin.json
msgctxt "description"
msgid "Helps you to install an 'export to Cura' button in Siemens NX."
-msgstr "Ayuda a instalar el botón para exportar a Cura en in Siemens NX."
+msgstr "Ayuda a instalar el botón para exportar a Cura en Siemens NX."
#: cura-siemensnx-plugin/plugin.json
msgctxt "name"
@@ -4769,12 +4794,12 @@ msgstr "Escritor de perfiles de Cura"
#: CuraPrintProfileCreator/plugin.json
msgctxt "description"
msgid "Allows material manufacturers to create new material and quality profiles using a drop-in UI."
-msgstr ""
+msgstr "Permite a los fabricantes de material crear nuevos perfiles de material y calidad mediante una IU integrada."
#: CuraPrintProfileCreator/plugin.json
msgctxt "name"
msgid "Print Profile Assistant"
-msgstr ""
+msgstr "Imprimir asistente del perfil"
#: 3MFWriter/plugin.json
msgctxt "description"
diff --git a/resources/i18n/es_ES/fdmprinter.def.json.po b/resources/i18n/es_ES/fdmprinter.def.json.po
index 82b913b3e8..98c2d1da7f 100644
--- a/resources/i18n/es_ES/fdmprinter.def.json.po
+++ b/resources/i18n/es_ES/fdmprinter.def.json.po
@@ -1,20 +1,21 @@
# Cura JSON setting files
-# Copyright (C) 2017 Ultimaker
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
-"PO-Revision-Date: 2017-11-30 13:05+0100\n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
+"PO-Revision-Date: 2018-02-12 13:40+0100\n"
"Last-Translator: Bothof \n"
"Language-Team: Spanish\n"
"Language: es_ES\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.0.6\n"
#: fdmprinter.def.json
msgctxt "machine_settings label"
@@ -97,7 +98,7 @@ msgstr "Elija si desea escribir un comando para esperar a que la temperatura de
#: fdmprinter.def.json
msgctxt "material_print_temp_wait label"
msgid "Wait for Nozzle Heatup"
-msgstr "Esperar a la que la tobera se caliente"
+msgstr "Esperar a que la tobera se caliente"
#: fdmprinter.def.json
msgctxt "material_print_temp_wait description"
@@ -352,12 +353,12 @@ msgstr "Repetier"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract label"
msgid "Firmware Retraction"
-msgstr ""
+msgstr "Retracción de firmware"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract description"
msgid "Whether to use firmware retract commands (G10/G11) instead of using the E property in G1 commands to retract the material."
-msgstr ""
+msgstr "Utilizar o no los comandos de retracción de firmware (G10/G11) en lugar de utilizar la propiedad E en comandos G1 para retraer el material."
#: fdmprinter.def.json
msgctxt "machine_disallowed_areas label"
@@ -412,12 +413,12 @@ msgstr "Diferencia de altura entre la punta de la tobera y el sistema del puente
#: fdmprinter.def.json
msgctxt "machine_nozzle_id label"
msgid "Nozzle ID"
-msgstr "Id. de la tobera"
+msgstr "ID de la tobera"
#: fdmprinter.def.json
msgctxt "machine_nozzle_id description"
msgid "The nozzle ID for an extruder train, such as \"AA 0.4\" and \"BB 0.8\"."
-msgstr "Id. de la tobera de un tren extrusor, como \"AA 0.4\" y \"BB 0.8\"."
+msgstr "ID de la tobera de un tren extrusor, como \"AA 0.4\" y \"BB 0.8\"."
#: fdmprinter.def.json
msgctxt "machine_nozzle_size label"
@@ -1007,7 +1008,7 @@ msgstr "Compensar superposiciones de pared"
#: fdmprinter.def.json
msgctxt "travel_compensate_overlapping_walls_enabled description"
msgid "Compensate the flow for parts of a wall being printed where there is already a wall in place."
-msgstr "Compensa el flujo en partes de una pared que se están imprimiendo dónde ya hay una pared."
+msgstr "Compensa el flujo en partes de una pared que se están imprimiendo donde ya hay una pared."
#: fdmprinter.def.json
msgctxt "travel_compensate_overlapping_walls_0_enabled label"
@@ -1052,12 +1053,12 @@ msgstr "En todas partes"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps label"
msgid "Filter Out Tiny Gaps"
-msgstr ""
+msgstr "Filtrar pequeños huecos"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps description"
msgid "Filter out tiny gaps to reduce blobs on outside of model."
-msgstr ""
+msgstr "Filtrar pequeños huecos para reducir las gotas en la parte externa del modelo."
#: fdmprinter.def.json
msgctxt "fill_outline_gaps label"
@@ -1347,7 +1348,7 @@ msgstr "Patrón de relleno"
#: fdmprinter.def.json
msgctxt "infill_pattern description"
msgid "The pattern of the infill material of the print. The line and zig zag infill swap direction on alternate layers, reducing material cost. The grid, triangle, tri-hexagon, cubic, octet, quarter cubic, cross and concentric patterns are fully printed every layer. Cubic, quarter cubic and octet infill change with every layer to provide a more equal distribution of strength over each direction."
-msgstr "Patrón del material de relleno de la impresión. El relleno de línea y zigzag cambia de dirección en capas alternas, con lo que se reduce el coste del material. Los patrones de rejilla, triángulo, trihexagonal, cúbico, de octeto, cúbico bitruncado y transversal y concéntrico se imprimen en todas las capas por completo. El relleno cúbico, cúbico bitruncado y de octeto cambian en cada capa para proporcionar una distribución de fuerza equitativa en cada dirección."
+msgstr "Patrón del material de relleno de la impresión. El relleno de línea y zigzag cambia de dirección en capas alternas, con lo que se reduce el coste del material. Los patrones de rejilla, triángulo, trihexagonal, cúbico, octeto, cúbico bitruncado y transversal y concéntrico se imprimen en todas las capas por completo. El relleno cúbico, cúbico bitruncado y octeto cambian en cada capa para proporcionar una distribución de fuerza equitativa en cada dirección."
#: fdmprinter.def.json
msgctxt "infill_pattern option grid"
@@ -1442,7 +1443,7 @@ msgstr "Desplazamiento del relleno sobre el eje X"
#: fdmprinter.def.json
msgctxt "infill_offset_x description"
msgid "The infill pattern is moved this distance along the X axis."
-msgstr ""
+msgstr "El patrón de relleno se mueve esta distancia a lo largo del eje X."
#: fdmprinter.def.json
msgctxt "infill_offset_y label"
@@ -1452,7 +1453,7 @@ msgstr "Desplazamiento del relleno sobre el eje X"
#: fdmprinter.def.json
msgctxt "infill_offset_y description"
msgid "The infill pattern is moved this distance along the Y axis."
-msgstr ""
+msgstr "El patrón de relleno se mueve esta distancia a lo largo del eje Y."
#: fdmprinter.def.json
msgctxt "sub_div_rad_add label"
@@ -1472,7 +1473,7 @@ msgstr "Porcentaje de superposición del relleno"
#: fdmprinter.def.json
msgctxt "infill_overlap description"
msgid "The amount of overlap between the infill and the walls as a percentage of the infill line width. A slight overlap allows the walls to connect firmly to the infill."
-msgstr ""
+msgstr "La cantidad de superposición entre el relleno y las paredes son un porcentaje del ancho de la línea de relleno. Una ligera superposición permite que las paredes estén firmemente unidas al relleno."
#: fdmprinter.def.json
msgctxt "infill_overlap_mm label"
@@ -1492,7 +1493,7 @@ msgstr "Porcentaje de superposición del forro"
#: fdmprinter.def.json
msgctxt "skin_overlap description"
msgid "The amount of overlap between the skin and the walls as a percentage of the skin line width. A slight overlap allows the walls to connect firmly to the skin. This is a percentage of the average line widths of the skin lines and the innermost wall."
-msgstr ""
+msgstr "La cantidad de superposición entre el forro y las paredes son un porcentaje del ancho de la línea de forro. Una ligera superposición permite que las paredes estén firmemente unidas al forro. Este es el porcentaje de la media de los anchos de las líneas del forro y la pared más profunda."
#: fdmprinter.def.json
msgctxt "skin_overlap_mm label"
@@ -1722,7 +1723,7 @@ msgstr "Temperatura de la placa de impresión"
#: fdmprinter.def.json
msgctxt "material_bed_temperature description"
msgid "The temperature used for the heated build plate. If this is 0, the bed temperature will not be adjusted."
-msgstr ""
+msgstr "La temperatura utilizada para la placa de impresión caliente. Si el valor es 0, la temperatura de la plataforma no se ajustará."
#: fdmprinter.def.json
msgctxt "material_bed_temperature_layer_0 label"
@@ -1792,7 +1793,7 @@ msgstr "Retracción en el cambio de capa"
#: fdmprinter.def.json
msgctxt "retract_at_layer_change description"
msgid "Retract the filament when the nozzle is moving to the next layer."
-msgstr "Retrae el filamento cuando la tobera se mueve a la siguiente capa. "
+msgstr "Retrae el filamento cuando la tobera se mueve a la siguiente capa."
#: fdmprinter.def.json
msgctxt "retraction_amount label"
@@ -4266,82 +4267,82 @@ msgstr "Experimental"
#: fdmprinter.def.json
msgctxt "support_tree_enable label"
msgid "Tree Support"
-msgstr ""
+msgstr "Soporte en árbol"
#: fdmprinter.def.json
msgctxt "support_tree_enable description"
msgid "Generate a tree-like support with branches that support your print. This may reduce material usage and print time, but greatly increases slicing time."
-msgstr ""
+msgstr "Generar un soporte en forma de árbol con ramas que apoyan la impresión. Esto puede reducir el uso de material y el tiempo de impresión, pero aumenta considerablemente el tiempo de fragmentación."
#: fdmprinter.def.json
msgctxt "support_tree_angle label"
msgid "Tree Support Branch Angle"
-msgstr ""
+msgstr "Ángulo de las ramas del soporte en árbol"
#: fdmprinter.def.json
msgctxt "support_tree_angle description"
msgid "The angle of the branches. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach."
-msgstr ""
+msgstr "El ángulo de las ramas. Utilice un ángulo inferior para que sean más verticales y estables. Utilice un ángulo superior para poder tener más alcance."
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance label"
msgid "Tree Support Branch Distance"
-msgstr ""
+msgstr "Distancia de las ramas del soporte en árbol"
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance description"
msgid "How far apart the branches need to be when they touch the model. Making this distance small will cause the tree support to touch the model at more points, causing better overhang but making support harder to remove."
-msgstr ""
+msgstr "Qué separación deben tener las ramas cuando tocan el modelo. Reducir esta distancia ocasionará que el soporte en árbol toque el modelo en más puntos, produciendo mejor cobertura pero dificultando la tarea de retirar el soporte."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter label"
msgid "Tree Support Branch Diameter"
-msgstr ""
+msgstr "Diámetro de las ramas del soporte en árbol"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter description"
msgid "The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this."
-msgstr ""
+msgstr "El diámetro de las ramas más finas del soporte en árbol. Cuanto más gruesas sean las ramas, más robustas serán. Las ramas que estén cerca de la base serán más gruesas que esto."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle label"
msgid "Tree Support Branch Diameter Angle"
-msgstr ""
+msgstr "Ángulo de diámetro de las ramas del soporte en árbol"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle description"
msgid "The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the tree support."
-msgstr ""
+msgstr "El ángulo del diámetro de las ramas es gradualmente más alto según se acercan a la base. Un ángulo de 0 ocasionará que las ramas tengan un grosor uniforme en toda su longitud. Un poco de ángulo puede aumentar la estabilidad del soporte en árbol."
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution label"
msgid "Tree Support Collision Resolution"
-msgstr ""
+msgstr "Resolución de colisión del soporte en árbol"
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution description"
msgid "Resolution to compute collisions with to avoid hitting the model. Setting this lower will produce more accurate trees that fail less often, but increases slicing time dramatically."
-msgstr ""
+msgstr "Resolución para computar colisiones para evitar golpear el modelo. Establecer un ajuste bajo producirá árboles más precisos que producen fallos con menor frecuencia, pero aumenta significativamente el tiempo de fragmentación."
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness label"
msgid "Tree Support Wall Thickness"
-msgstr ""
+msgstr "Grosor de las paredes del soporte en árbol"
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness description"
msgid "The thickness of the walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "El grosor de las paredes de las ramas del soporte en árbol. Imprimir paredes más gruesas llevará más tiempo pero no se caerán tan fácilmente."
#: fdmprinter.def.json
msgctxt "support_tree_wall_count label"
msgid "Tree Support Wall Line Count"
-msgstr ""
+msgstr "Recuento de líneas de pared del soporte en árbol"
#: fdmprinter.def.json
msgctxt "support_tree_wall_count description"
msgid "The number of walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "El número de las paredes de las ramas del soporte en árbol. Imprimir paredes más gruesas llevará más tiempo pero no se caerán tan fácilmente."
#: fdmprinter.def.json
msgctxt "slicing_tolerance label"
@@ -4416,12 +4417,12 @@ msgstr "Una lista de los valores enteros de las direcciones de línea si las cap
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization label"
msgid "Infill Travel Optimization"
-msgstr ""
+msgstr "Optimización del desplazamiento del relleno"
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization description"
msgid "When enabled, the order in which the infill lines are printed is optimized to reduce the distance travelled. The reduction in travel time achieved very much depends on the model being sliced, infill pattern, density, etc. Note that, for some models that have many small areas of infill, the time to slice the model may be greatly increased."
-msgstr ""
+msgstr "Cuando está habilitado, se optimiza el orden en el que se imprimen las líneas de relleno para reducir la distancia de desplazamiento. La reducción del tiempo de desplazamiento obtenido depende en gran parte del modelo que se está fragmentando, el patrón de relleno, la densidad, etc. Tenga en cuenta que, para algunos modelos que tienen pequeñas áreas de relleno, el tiempo para fragmentar el modelo se puede aumentar en gran medida."
#: fdmprinter.def.json
msgctxt "material_flow_dependent_temperature label"
@@ -5055,42 +5056,42 @@ msgstr "Distancia entre la tobera y líneas descendentes en horizontal. Cuanto m
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled label"
msgid "Use adaptive layers"
-msgstr ""
+msgstr "Utilizar capas de adaptación"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled description"
msgid "Adaptive layers computes the layer heights depending on the shape of the model."
-msgstr ""
+msgstr "Las capas de adaptación calculan las alturas de las capas dependiendo de la forma del modelo."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation label"
msgid "Adaptive layers maximum variation"
-msgstr ""
+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 in mm."
-msgstr ""
+msgstr "La diferencia de altura máxima permitida en comparación con la altura de la capa base en mm."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step label"
msgid "Adaptive layers variation step size"
-msgstr ""
+msgstr "Tamaño de pasos de variación de las capas de adaptación"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step description"
msgid "The difference in height of the next layer height compared to the previous one."
-msgstr ""
+msgstr "La diferencia de altura de la siguiente altura de capa en comparación con la anterior."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold label"
msgid "Adaptive layers threshold"
-msgstr ""
+msgstr "Umbral de las capas de adaptación"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold description"
msgid "Threshold whether to use a smaller layer or not. This number is compared to the tan of the steepest slope in a layer."
-msgstr ""
+msgstr "Umbral para usar o no una capa más pequeña. Este número se compara con el curtido de la pendiente más empinada de una capa."
#: fdmprinter.def.json
msgctxt "command_line_settings label"
diff --git a/resources/i18n/fr_FR/cura.po b/resources/i18n/fr_FR/cura.po
index 0504b1e50c..ee4e175f8a 100644
--- a/resources/i18n/fr_FR/cura.po
+++ b/resources/i18n/fr_FR/cura.po
@@ -1,20 +1,22 @@
# Cura
-# Copyright (C) 2017 Ultimaker
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
-"PO-Revision-Date: 2017-11-30 13:05+0100\n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
+"PO-Revision-Date: 2018-02-13 17:26+0100\n"
"Last-Translator: Bothof \n"
"Language-Team: French\n"
"Language: fr_FR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Poedit 2.0.6\n"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:26
msgctxt "@action"
@@ -105,12 +107,12 @@ msgstr "Afficher le récapitulatif des changements"
#: /home/ruben/Projects/Cura/plugins/ProfileFlattener/ProfileFlattener.py:20
msgctxt "@item:inmenu"
msgid "Flatten active settings"
-msgstr "Aplatir les paramètres actifs"
+msgstr "Réduire les paramètres actifs"
#: /home/ruben/Projects/Cura/plugins/ProfileFlattener/ProfileFlattener.py:32
msgctxt "@info:status"
msgid "Profile has been flattened & activated."
-msgstr "Le profil a été aplati et activé."
+msgstr "Le profil a été réduit et activé."
#: /home/ruben/Projects/Cura/plugins/USBPrinting/USBPrinterOutputDevice.py:27
msgctxt "@item:inmenu"
@@ -145,7 +147,7 @@ msgstr "Imprimante indisponible"
#: /home/ruben/Projects/Cura/plugins/USBPrinting/USBPrinterOutputDevice.py:485
msgctxt "@info:status"
msgid "This printer does not support USB printing because it uses UltiGCode flavor."
-msgstr "L'imprimante ne prend pas en charge l'impression par USB car elle utilise UltiGCode parfum."
+msgstr "L'imprimante ne prend pas en charge l'impression par USB car elle utilise UltiGCode."
#: /home/ruben/Projects/Cura/plugins/USBPrinting/USBPrinterOutputDevice.py:485
msgctxt "@info:title"
@@ -187,7 +189,7 @@ msgstr "Firmware de l'imprimante"
#: /home/ruben/Projects/Cura/plugins/PrepareStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Prepare"
-msgstr ""
+msgstr "Préparer"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:23
msgctxt "@action:button Preceded by 'Ready to'."
@@ -209,7 +211,7 @@ msgstr "Enregistrement sur le lecteur amovible {0}"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:89
msgctxt "@info:title"
msgid "Saving"
-msgstr "Enregistrement..."
+msgstr "Enregistrement"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:99
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:102
@@ -267,7 +269,7 @@ msgstr "Ejecter le lecteur amovible {0}"
#, python-brace-format
msgctxt "@info:status"
msgid "Ejected {0}. You can now safely remove the drive."
-msgstr "Lecteur {0} éjecté. Vous pouvez maintenant le retirer en tout sécurité."
+msgstr "Lecteur {0} éjecté. Vous pouvez maintenant le retirer en toute sécurité."
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:156
msgctxt "@info:title"
@@ -372,22 +374,22 @@ msgstr "Connecté sur le réseau. Pas d'accès pour commander l'imprimante."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:410
msgctxt "@info:status"
msgid "Access request was denied on the printer."
-msgstr "La demande d'accès a été refusée sur l'imprimante."
+msgstr "La demande d'accès à l'imprimante a été refusée."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:413
msgctxt "@info:status"
msgid "Access request failed due to a timeout."
-msgstr "Échec de la demande d'accès à cause de la durée limite."
+msgstr "Durée d'attente dépassée. Échec de la demande d'accès."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:478
msgctxt "@info:status"
msgid "The connection with the network was lost."
-msgstr "La connexion avec le réseau a été perdue."
+msgstr "Interruption de connexion au le réseau."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:501
msgctxt "@info:status"
msgid "The connection with the printer was lost. Check your printer to see if it is connected."
-msgstr "La connexion avec l'imprimante a été perdue. Vérifiez que votre imprimante est connectée."
+msgstr "La connexion avec l'imprimante est interrompue. Vérifiez que votre imprimante est connectée."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:666
#, python-format
@@ -416,19 +418,19 @@ msgstr "Impossible de démarrer une nouvelle tâche d'impression car l'imprimant
#, python-brace-format
msgctxt "@label"
msgid "Not enough material for spool {0}."
-msgstr "Pas suffisamment de matériau pour bobine {0}."
+msgstr "Pas suffisamment de matériau pour la bobine {0}."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:720
#, python-brace-format
msgctxt "@label"
msgid "Different PrintCore (Cura: {0}, Printer: {1}) selected for extruder {2}"
-msgstr "PrintCore différent (Cura : {0}, Imprimante : {1}) sélectionné pour l'extrudeuse {2}"
+msgstr "PrintCore différent (Cura : {0}, Imprimante : {1}) sélectionné pour l'extrudeur {2}"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:734
#, python-brace-format
msgctxt "@label"
msgid "Different material (Cura: {0}, Printer: {1}) selected for extruder {2}"
-msgstr "Matériau différent (Cura : {0}, Imprimante : {1}) sélectionné pour l'extrudeuse {2}"
+msgstr "Matériau différent (Cura : {0}, Imprimante : {1}) sélectionné pour l'extrudeur {2}"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:742
#, python-brace-format
@@ -444,7 +446,7 @@ msgstr "Êtes-vous sûr(e) de vouloir imprimer avec la configuration sélectionn
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:748
msgctxt "@label"
msgid "There is a mismatch between the configuration or calibration of the printer and Cura. For the best result, always slice for the PrintCores and materials that are inserted in your printer."
-msgstr "Problème de compatibilité entre la configuration ou l'étalonnage de l'imprimante et Cura. Pour un résultat optimal, découpez toujours pour les PrintCores et matériaux insérés dans votre imprimante."
+msgstr "Problème de compatibilité entre la configuration ou la calibration de l'imprimante et Cura. Pour un résultat optimal, découpez toujours les PrintCores et matériaux insérés dans votre imprimante."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:754
msgctxt "@window:title"
@@ -465,7 +467,7 @@ msgstr "Envoi des données à l'imprimante"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:874
msgctxt "@info:title"
msgid "Sending Data"
-msgstr "Envoi des données..."
+msgstr "Envoi des données"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:945
msgctxt "@info:status"
@@ -501,12 +503,12 @@ msgstr "Synchroniser avec votre imprimante"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:1293
msgctxt "@label"
msgid "Would you like to use your current printer configuration in Cura?"
-msgstr "Voulez-vous utiliser votre configuration d'imprimante actuelle dans Cura ?"
+msgstr "Voulez-vous utiliser votre configuration actuelle d'imprimante dans Cura ?"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:1295
msgctxt "@label"
msgid "The PrintCores and/or materials on your printer differ from those within your current project. For the best result, always slice for the PrintCores and materials that are inserted in your printer."
-msgstr "Les PrintCores et / ou matériaux sur votre imprimante diffèrent de ceux de votre projet actuel. Pour un résultat optimal, découpez toujours pour les PrintCores et matériaux insérés dans votre imprimante."
+msgstr "Les PrintCores et / ou les matériaux sur votre imprimante sont différents de ceux de votre projet actuel. Pour un résultat optimal, découpez toujours les PrintCores et matériaux insérés dans votre imprimante."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:112
msgid "This printer is not set up to host a group of connected Ultimaker 3 printers."
@@ -521,7 +523,7 @@ msgstr "L'imprimante n'est pas configurée pour héberger un groupe de {count} i
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:114
#, python-brace-format
msgid "{printer_name} has finished printing '{job_name}'. Please collect the print and confirm clearing the build plate."
-msgstr "{printer_name} a terminé d'imprimer '{job_name}'. Veuillez enlever l'impression et confirmer avoir débarrassé le plateau."
+msgstr "{printer_name} a terminé d'imprimer '{job_name}'. Veuillez enlever l'impression et confirmer avoir nettoyé le plateau."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:115
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:533
@@ -560,7 +562,7 @@ msgstr "Ouvre l'interface d'impression des tâches dans votre navigateur."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:239
msgctxt "@label Printer name"
msgid "Unknown"
-msgstr ""
+msgstr "Inconnu"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:505
#, python-brace-format
@@ -595,13 +597,13 @@ msgstr "Connecter via le réseau"
#: /home/ruben/Projects/Cura/plugins/MonitorStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Monitor"
-msgstr ""
+msgstr "Surveiller"
#: /home/ruben/Projects/Cura/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py:66
#, python-brace-format
msgctxt "@info Don't translate {machine_name}, since it gets replaced by a printer name!"
msgid "New features are available for your {machine_name}! It is recommended to update the firmware on your printer."
-msgstr "De nouvelles fonctionnalités sont disponibles pour votre {machine_name} ! Il est recommandé de mettre à jour le firmware sur votre imprimante."
+msgstr "De nouvelles fonctionnalités sont disponibles pour votre {machine_name} ! Il est recommandé de mettre à jour le firmware de votre imprimante."
#: /home/ruben/Projects/Cura/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py:67
#, python-format
@@ -622,7 +624,7 @@ msgstr "Impossible d'accéder aux informations de mise à jour."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:579
msgctxt "@info:status"
msgid "SolidWorks reported errors, while opening your file. We recommend to solve these issues inside SolidWorks itself."
-msgstr ""
+msgstr "SolidWorks a signalé des erreurs lors de l'ouverture de votre fichier. Nous vous recommandons de résoudre ces problèmes dans SolidWorks lui-même."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:591
msgctxt "@info:status"
@@ -631,6 +633,9 @@ msgid ""
"\n"
" Thanks!."
msgstr ""
+"Aucun modèle n'a été trouvé à l'intérieur de votre dessin. Pouvez-vous vérifier son contenu de nouveau et vous assurer qu'une pièce ou un assemblage est présent ?\n"
+"\n"
+" Merci !"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:595
msgctxt "@info:status"
@@ -639,6 +644,9 @@ msgid ""
"\n"
"Sorry!"
msgstr ""
+"Plus d'une pièce ou d'un ensemble de pièces ont été trouvés dans votre dessin. Nous ne prenons actuellement en charge que les dessins comptant exactement une pièce ou un ensemble de pièces.\n"
+"\n"
+"Désolé !"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:25
msgctxt "@item:inlistbox"
@@ -653,7 +661,7 @@ msgstr "Fichier d'assemblage SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:33
msgctxt "@item:inlistbox"
msgid "SolidWorks drawing file"
-msgstr ""
+msgstr "Fichier de dessin SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:48
msgctxt "@info:status"
@@ -664,6 +672,11 @@ msgid ""
"With kind regards\n"
" - Thomas Karl Pietrowski"
msgstr ""
+"Cher client,\n"
+"Nous n'avons pas pu trouver une installation valide de SolidWorks sur votre système. Cela signifie soit que SolidWorks n'est pas installé, soit que vous ne possédez pas de licence valide. Veuillez vous assurer que l'exécution de SolidWorks lui-même fonctionne sans problèmes et / ou contactez votre service IT.\n"
+"\n"
+"Cordialement,\n"
+" - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:57
msgctxt "@info:status"
@@ -674,6 +687,11 @@ msgid ""
"With kind regards\n"
" - Thomas Karl Pietrowski"
msgstr ""
+"Cher client,\n"
+"Vous exécutez actuellement ce plug-in sur un système d'exploitation autre que Windows. Ce plug-in fonctionne uniquement sous Windows et lorsque SolidWorks est installé avec une licence valide. Veuillez installer ce plug-in sur un poste Windows où SolidWorks est installé.\n"
+"\n"
+"Cordialement,\n"
+" - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:70
msgid "Configure"
@@ -681,7 +699,7 @@ msgstr "Configurer"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:71
msgid "Installation guide for SolidWorks macro"
-msgstr ""
+msgstr "Guide d'installation SolidWorks macro"
#: /home/ruben/Projects/Cura/plugins/SimulationView/__init__.py:14
msgctxt "@item:inlistbox"
@@ -705,32 +723,32 @@ msgstr "Modifier le G-Code"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:43
msgctxt "@info"
msgid "Cura collects anonymized usage statistics."
-msgstr ""
+msgstr "Cura recueille des statistiques d'utilisation anonymes."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:46
msgctxt "@info:title"
msgid "Collecting Data"
-msgstr "Collecte des données..."
+msgstr "Collecte de données"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:48
msgctxt "@action:button"
msgid "Allow"
-msgstr ""
+msgstr "Autoriser"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:49
msgctxt "@action:tooltip"
msgid "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing."
-msgstr ""
+msgstr "Autoriser Cura à envoyer des statistiques d'utilisation anonymes pour mieux prioriser les améliorations futures apportées à Cura. Certaines de vos préférences et paramètres sont envoyés, ainsi que la version du logiciel Cura et un hachage des modèles que vous découpez."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:50
msgctxt "@action:button"
msgid "Disable"
-msgstr ""
+msgstr "Désactiver"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:51
msgctxt "@action:tooltip"
msgid "Don't allow Cura to send anonymized usage statistics. You can enable it again in the preferences."
-msgstr ""
+msgstr "Ne pas autoriser Cura à envoyer des statistiques d'utilisation anonymes. Vous pouvez modifier ce paramètre dans les préférences."
#: /home/ruben/Projects/Cura/plugins/LegacyProfileReader/__init__.py:14
msgctxt "@item:inlistbox"
@@ -740,7 +758,7 @@ msgstr "Profils Cura 15.04"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/__init__.py:15
msgctxt "@item:inlistbox"
msgid "Blender file"
-msgstr ""
+msgstr "Fichier Blender"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/CadIntegrationUtils/CommonReader.py:199
msgctxt "@info:status"
@@ -748,6 +766,8 @@ msgid ""
"Could not export using \"{}\" quality!\n"
"Felt back to \"{}\"."
msgstr ""
+"Impossible d'exporter avec la qualité \"{}\" !\n"
+"Qualité redéfinie sur \"{}\"."
#: /home/ruben/Projects/Cura/plugins/GCodeProfileReader/__init__.py:14
#: /home/ruben/Projects/Cura/plugins/GCodeReader/__init__.py:14
@@ -934,12 +954,12 @@ msgstr "Profil Cura"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:12
msgctxt "@item:inmenu"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Assistant de profil"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:17
msgctxt "@item:inlistbox"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Assistant de profil"
#: /home/ruben/Projects/Cura/plugins/3MFWriter/__init__.py:30
msgctxt "@item:inlistbox"
@@ -965,12 +985,12 @@ msgstr "Mise à niveau du firmware"
#: /home/ruben/Projects/Cura/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py:14
msgctxt "@action"
msgid "Checkup"
-msgstr "Check-up"
+msgstr "Vérification"
#: /home/ruben/Projects/Cura/plugins/UltimakerMachineActions/BedLevelMachineAction.py:15
msgctxt "@action"
msgid "Level build plate"
-msgstr "Nivellement du plateau"
+msgstr "Paramétrage du plateau de fabrication"
#: /home/ruben/Projects/Cura/cura/PrintInformation.py:98
msgctxt "@tooltip"
@@ -1010,7 +1030,7 @@ msgstr "Support"
#: /home/ruben/Projects/Cura/cura/PrintInformation.py:105
msgctxt "@tooltip"
msgid "Skirt"
-msgstr "Jupe"
+msgstr "Contourner"
#: /home/ruben/Projects/Cura/cura/PrintInformation.py:106
msgctxt "@tooltip"
@@ -1074,7 +1094,7 @@ msgstr "Matériau personnalisé"
#: /home/ruben/Projects/Cura/cura/Settings/ExtrudersModel.py:205
msgctxt "@menuitem"
msgid "Not overridden"
-msgstr "Pas écrasé"
+msgstr "Pas pris en compte"
#: /home/ruben/Projects/Cura/cura/Settings/MachineManager.py:124
msgctxt "@info:status"
@@ -1137,13 +1157,13 @@ msgstr "Échec de l'importation du profil depuis le fichier {0} or !"
msgid "This profile {0} contains incorrect data, could not import it."
-msgstr ""
+msgstr "Le profil {0} contient des données incorrectes ; échec de l'importation."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:240
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "The machine defined in profile {0} doesn't match with your current machine, could not import it."
-msgstr ""
+msgstr "La machine définie dans le profil {0} ne correspond pas à votre machine actuelle ; échec de l'importation."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:274
#, python-brace-format
@@ -1155,7 +1175,7 @@ msgstr "Importation du profil {0} réussie"
#, python-brace-format
msgctxt "@info:status"
msgid "File {0} does not contain any valid profile."
-msgstr ""
+msgstr "Le fichier {0} ne contient pas de profil valide."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:280
#, python-brace-format
@@ -1183,7 +1203,7 @@ msgstr "Impossible de trouver un type de qualité {0} pour la configuration actu
#, python-brace-format
msgctxt "@label"
msgid "Group #{group_nr}"
-msgstr ""
+msgstr "Groupe nº {group_nr}"
#: /home/ruben/Projects/Cura/cura/BuildVolume.py:100
msgctxt "@info:status"
@@ -1204,7 +1224,7 @@ msgstr "Multiplication et placement d'objets"
#: /home/ruben/Projects/Cura/cura/MultiplyObjectsJob.py:78
msgctxt "@info:title"
msgid "Placing Object"
-msgstr "Placement de l'objet..."
+msgstr "Placement de l'objet"
#: /home/ruben/Projects/Cura/cura/MultiplyObjectsJob.py:78
#: /home/ruben/Projects/Cura/cura/Arranging/ArrangeObjectsJob.py:88
@@ -1243,6 +1263,9 @@ msgid ""
"
Please use the \"Send report\" button to post a bug report automatically to our servers
\n"
" "
msgstr ""
+"
Une erreur fatale s'est produite. Veuillez nous envoyer ce Rapport d'incident pour résoudre le problème
\n"
+"
Veuillez utiliser le bouton « Envoyer rapport » pour publier automatiquement un rapport d'erreur sur nos serveurs
"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:147
msgctxt "@title:groupbox"
msgid "Error traceback"
-msgstr ""
+msgstr "Retraçage de l'erreur"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:214
msgctxt "@title:groupbox"
msgid "Logs"
-msgstr "Journaux"
+msgstr "Registres"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:237
msgctxt "@title:groupbox"
@@ -1330,7 +1353,7 @@ msgstr "Chargement des machines..."
#: /home/ruben/Projects/Cura/cura/CuraApplication.py:660
msgctxt "@info:progress"
msgid "Setting up scene..."
-msgstr "Préparation de la scène..."
+msgstr "Préparation de la tâche..."
#: /home/ruben/Projects/Cura/cura/CuraApplication.py:702
msgctxt "@info:progress"
@@ -1481,7 +1504,7 @@ msgstr "La différence de hauteur entre la pointe de la buse et le système de p
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:254
msgctxt "@label"
msgid "Number of Extruders"
-msgstr "Nombre d'extrudeuses"
+msgstr "Nombre d'extrudeurs"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:310
msgctxt "@label"
@@ -1516,7 +1539,7 @@ msgstr "Taille de la buse"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:393
msgctxt "@label"
msgid "Compatible material diameter"
-msgstr ""
+msgstr "Diamètre du matériau compatible"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:395
msgctxt "@tooltip"
@@ -1536,12 +1559,12 @@ msgstr "Décalage buse Y"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:444
msgctxt "@label"
msgid "Extruder Start Gcode"
-msgstr "Extrudeuse Gcode de démarrage"
+msgstr "Extrudeur Gcode de démarrage"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:462
msgctxt "@label"
msgid "Extruder End Gcode"
-msgstr "Extrudeuse Gcode de fin"
+msgstr "Extrudeur Gcode de fin"
#: /home/ruben/Projects/Cura/plugins/ChangeLogPlugin/ChangeLog.qml:18
msgctxt "@label"
@@ -1661,12 +1684,12 @@ msgstr "Type"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:233
msgctxt "@label Printer name"
msgid "Ultimaker 3"
-msgstr ""
+msgstr "Ultimaker 3"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:236
msgctxt "@label Printer name"
msgid "Ultimaker 3 Extended"
-msgstr ""
+msgstr "Ultimaker 3 Extended"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:252
msgctxt "@label"
@@ -1728,12 +1751,12 @@ msgstr "Imprimer"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml:36
msgctxt "@label: arg 1 is group name"
msgid "%1 is not set up to host a group of connected Ultimaker 3 printers"
-msgstr "%1 n'est pas configurée pour héberger un groupe d'imprimantes connectées Ultimaker 3."
+msgstr "%1 n'est pas configurée pour héberger un groupe d'imprimantes connectées Ultimaker 3"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml:55
msgctxt "@label link to connect manager"
msgid "Add/Remove printers"
-msgstr ""
+msgstr "Ajouter / supprimer une imprimante"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/OpenPanelButton.qml:14
msgctxt "@info:tooltip"
@@ -1751,7 +1774,7 @@ msgstr "Afficher les tâches d'impression"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:408
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:39
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:271
@@ -1773,7 +1796,7 @@ msgstr "Connexion avec l'imprimante perdue"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:47
msgctxt "@label Printer status"
msgid "Unknown"
-msgstr ""
+msgstr "Inconnu"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:257
msgctxt "@label:status"
@@ -1869,62 +1892,62 @@ msgstr "Activer la configuration"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:21
msgctxt "@title:window"
msgid "SolidWorks: Export wizard"
-msgstr ""
+msgstr "SolidWorks : assistant d'exportation"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:45
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:140
msgctxt "@action:label"
msgid "Quality:"
-msgstr ""
+msgstr "Qualité :"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:78
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:179
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (3D-printing)"
-msgstr ""
+msgstr "Fine (impression 3D)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:79
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:180
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (3D-printing)"
-msgstr ""
+msgstr "Grossière (impression 3D)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:80
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:181
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (SolidWorks)"
-msgstr ""
+msgstr "Fine (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:81
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:182
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (SolidWorks)"
-msgstr ""
+msgstr "Grossière (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:94
msgctxt "@text:window"
msgid "Show this dialog again"
-msgstr ""
+msgstr "Afficher de nouveau cette boîte de dialogue"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:104
msgctxt "@action:button"
msgid "Continue"
-msgstr ""
+msgstr "Continuer"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:116
msgctxt "@action:button"
msgid "Abort"
-msgstr ""
+msgstr "Abandonner"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:21
msgctxt "@title:window"
msgid "How to install Cura SolidWorks macro"
-msgstr ""
+msgstr "Modalités d’installation de Cura SolidWorks macro"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:62
msgctxt "@description:label"
msgid "Steps:"
-msgstr ""
+msgstr "Procédure :"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:140
msgctxt "@action:button"
@@ -1932,101 +1955,103 @@ msgid ""
"Open the directory\n"
"with macro and icon"
msgstr ""
+"Ouvrez le répertoire\n"
+"contenant la macro et l'icône"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:160
msgctxt "@description:label"
msgid "Instructions:"
-msgstr ""
+msgstr "Instructions :"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:202
msgctxt "@action:playpause"
msgid "Play"
-msgstr ""
+msgstr "Jouer"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:206
msgctxt "@action:playpause"
msgid "Pause"
-msgstr ""
+msgstr "Pause"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:268
msgctxt "@action:button"
msgid "Previous Step"
-msgstr ""
+msgstr "Étape précédente"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:283
msgctxt "@action:button"
msgid "Done"
-msgstr ""
+msgstr "Terminé"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:287
msgctxt "@action:button"
msgid "Next Step"
-msgstr ""
+msgstr "Étape suivante"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:21
msgctxt "@title:window"
msgid "SolidWorks plugin: Configuration"
-msgstr ""
+msgstr "Plug-in SolidWorks : configuration"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:39
msgctxt "@title:tab"
msgid "Conversion settings"
-msgstr ""
+msgstr "Paramètres de conversion"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:66
msgctxt "@label"
msgid "First choice:"
-msgstr ""
+msgstr "Premier choix :"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:86
msgctxt "@text:menu"
msgid "Latest installed version (Recommended)"
-msgstr ""
+msgstr "Dernière version installée (recommandé)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:95
msgctxt "@text:menu"
msgid "Default version"
-msgstr ""
+msgstr "Version par défaut"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:193
msgctxt "@label"
msgid "Show wizard before opening SolidWorks files"
-msgstr ""
+msgstr "Afficher l'Assistant avant d'ouvrir les fichiers SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:203
msgctxt "@label"
msgid "Automatically rotate opened file into normed orientation"
-msgstr ""
+msgstr "Rotation automatique du fichier ouvert en orientation normalisée"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:210
msgctxt "@title:tab"
msgid "Installation(s)"
-msgstr ""
+msgstr "Installation(s)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:284
msgctxt "@label"
msgid "COM service found"
-msgstr ""
+msgstr "Service COM trouvé"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:295
msgctxt "@label"
msgid "Executable found"
-msgstr ""
+msgstr "Fichier exécutable trouvé"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:306
msgctxt "@label"
msgid "COM starting"
-msgstr ""
+msgstr "Lancement de COM"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:317
msgctxt "@label"
msgid "Revision number"
-msgstr ""
+msgstr "Numéro de révision"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:328
msgctxt "@label"
msgid "Functions available"
-msgstr ""
+msgstr "Fonctions disponibles"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:341
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:264
@@ -2107,12 +2132,12 @@ msgstr "Paroi interne"
#: /home/ruben/Projects/Cura/plugins/SimulationView/SimulationView.qml:410
msgctxt "@label"
msgid "min"
-msgstr "min."
+msgstr "min"
#: /home/ruben/Projects/Cura/plugins/SimulationView/SimulationView.qml:452
msgctxt "@label"
msgid "max"
-msgstr "max."
+msgstr "max"
#: /home/ruben/Projects/Cura/plugins/PostProcessingPlugin/PostProcessingPlugin.qml:18
msgctxt "@title:window"
@@ -2212,32 +2237,32 @@ msgstr "Lissage"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:38
msgctxt "@label"
msgid "Mesh Type"
-msgstr ""
+msgstr "Type de maille"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:69
msgctxt "@label"
msgid "Normal model"
-msgstr ""
+msgstr "Modèle normal"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:76
msgctxt "@label"
msgid "Print as support"
-msgstr ""
+msgstr "Imprimer comme support"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:84
msgctxt "@label"
msgid "Don't support overlap with other models"
-msgstr ""
+msgstr "Ne pas prendre en charge le chevauchement avec d'autres modèles"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:92
msgctxt "@label"
msgid "Modify settings for overlap with other models"
-msgstr ""
+msgstr "Modifier les paramètres de chevauchement avec d'autres modèles"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:100
msgctxt "@label"
msgid "Modify settings for infill of other models"
-msgstr ""
+msgstr "Modifier les paramètres de remplissage d'autres modèles"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:333
msgctxt "@action:button"
@@ -2279,7 +2304,7 @@ msgstr "Créer"
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:72
msgctxt "@action:title"
msgid "Summary - Cura Project"
-msgstr "Résumé - Projet Cura"
+msgstr "Sommaire - Projet Cura"
#: /home/ruben/Projects/Cura/plugins/3MFReader/WorkspaceDialog.qml:92
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:90
@@ -2290,7 +2315,7 @@ msgstr "Paramètres de l'imprimante"
#: /home/ruben/Projects/Cura/plugins/3MFReader/WorkspaceDialog.qml:108
msgctxt "@info:tooltip"
msgid "How should the conflict in the machine be resolved?"
-msgstr "Comment le conflit de la machine doit-il être résolu ?"
+msgstr "Comment le problème de la machine doit-il être résolu ?"
#: /home/ruben/Projects/Cura/plugins/3MFReader/WorkspaceDialog.qml:128
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:99
@@ -2316,7 +2341,7 @@ msgstr "Paramètres de profil"
#: /home/ruben/Projects/Cura/plugins/3MFReader/WorkspaceDialog.qml:181
msgctxt "@info:tooltip"
msgid "How should the conflict in the profile be resolved?"
-msgstr "Comment le conflit du profil doit-il être résolu ?"
+msgstr "Comment le problème du profil doit-il être résolu ?"
#: /home/ruben/Projects/Cura/plugins/3MFReader/WorkspaceDialog.qml:216
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:174
@@ -2352,7 +2377,7 @@ msgstr "Paramètres du matériau"
#: /home/ruben/Projects/Cura/plugins/3MFReader/WorkspaceDialog.qml:269
msgctxt "@info:tooltip"
msgid "How should the conflict in the material be resolved?"
-msgstr "Comment le conflit du matériau doit-il être résolu ?"
+msgstr "Comment le problème du matériau doit-il être résolu ?"
#: /home/ruben/Projects/Cura/plugins/3MFReader/WorkspaceDialog.qml:312
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:209
@@ -2415,7 +2440,7 @@ msgstr "Télécharger"
#: /home/ruben/Projects/Cura/plugins/PluginBrowser/PluginBrowser.qml:199
msgctxt "@title:window"
msgid "Plugin License Agreement"
-msgstr "Plug-in d'accord de licence"
+msgstr "Plug-in de l'accord de licence"
#: /home/ruben/Projects/Cura/plugins/PluginBrowser/PluginBrowser.qml:220
msgctxt "@label"
@@ -2615,7 +2640,7 @@ msgstr "Contrôlée"
#: /home/ruben/Projects/Cura/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml:284
msgctxt "@label"
msgid "Everything is in order! You're done with your CheckUp."
-msgstr "Tout est en ordre ! Vous avez terminé votre check-up."
+msgstr "Tout est en ordre ! Vous avez terminé votre vérification."
#: /home/ruben/Projects/Cura/resources/qml/MonitorButton.qml:87
msgctxt "@label:MonitorStatus"
@@ -2631,7 +2656,7 @@ msgstr "L'imprimante n'accepte pas les commandes"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:194
msgctxt "@label:MonitorStatus"
msgid "In maintenance. Please check the printer"
-msgstr "En maintenance. Vérifiez l'imprimante"
+msgstr "En maintenance. Veuillez vérifier l'imprimante"
#: /home/ruben/Projects/Cura/resources/qml/MonitorButton.qml:102
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:184
@@ -2654,7 +2679,7 @@ msgstr "Préparation..."
#: /home/ruben/Projects/Cura/resources/qml/MonitorButton.qml:110
msgctxt "@label:MonitorStatus"
msgid "Please remove the print"
-msgstr "Supprimez l'imprimante"
+msgstr "Veuillez supprimer l'imprimante"
#: /home/ruben/Projects/Cura/resources/qml/MonitorButton.qml:241
msgctxt "@label:"
@@ -3097,27 +3122,27 @@ msgstr "Envoyer des informations (anonymes) sur l'impression"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:674
msgctxt "@label"
msgid "Experimental"
-msgstr ""
+msgstr "Expérimental"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:680
msgctxt "@info:tooltip"
msgid "Use multi build plate functionality"
-msgstr ""
+msgstr "Utiliser la fonctionnalité multi-plateau"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:685
msgctxt "@option:check"
msgid "Use multi build plate functionality (restart required)"
-msgstr ""
+msgstr "Utiliser la fonctionnalité multi-plateau (redémarrage requis)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:694
msgctxt "@info:tooltip"
msgid "Should newly loaded models be arranged on the build plate? Used in conjunction with multi build plate (EXPERIMENTAL)"
-msgstr ""
+msgstr "Les modèles nouvellement chargés doivent-ils être disposés sur le plateau ? Utilisé en conjonction avec le multi-plateau (EXPÉRIMENTAL)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:699
msgctxt "@option:check"
msgid "Do not arrange objects on load"
-msgstr ""
+msgstr "Ne pas réorganiser les objets lors du chargement"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:15
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:514
@@ -3383,7 +3408,7 @@ msgstr "Générateur GCode"
#: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:121
msgctxt "@label"
msgid "Interprocess communication library"
-msgstr "Bibliothèque de communication interprocess"
+msgstr "Bibliothèque de communication inter-process"
#: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:123
msgctxt "@label"
@@ -3530,7 +3555,7 @@ msgstr "Touché par"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:156
msgctxt "@label"
msgid "This setting is always shared between all extruders. Changing it here will change the value for all extruders."
-msgstr ""
+msgstr "Ce paramètre est toujours partagé par tous les extrudeurs. Le modifier ici entraînera la modification de la valeur pour tous les extrudeurs."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:159
msgctxt "@label"
@@ -3581,7 +3606,7 @@ msgstr "00h 00min"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:359
msgctxt "@tooltip"
msgid "Time specification"
-msgstr ""
+msgstr "Spécification de temps"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:441
msgctxt "@label"
@@ -3638,12 +3663,12 @@ msgstr "&Visualisation"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:37
msgctxt "@action:inmenu menubar:view"
msgid "&Camera position"
-msgstr ""
+msgstr "Position de la &caméra"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:52
msgctxt "@action:inmenu menubar:view"
msgid "&Build plate"
-msgstr ""
+msgstr "&Plateau"
#: /home/ruben/Projects/Cura/resources/qml/Menus/NozzleMenu.qml:40
msgctxt "@title:menuitem %1 is the nozzle currently loaded in the printer"
@@ -3683,7 +3708,7 @@ msgstr "Aucune imprimante n'est connectée"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:139
msgctxt "@label"
msgid "Extruder"
-msgstr "Extrudeuse"
+msgstr "Extrudeur"
#: /home/ruben/Projects/Cura/resources/qml/PrintMonitor.qml:120
msgctxt "@tooltip"
@@ -3813,27 +3838,27 @@ msgstr "&Quitter"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:114
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
-msgstr ""
+msgstr "Vue &3D"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:121
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
-msgstr ""
+msgstr "Vue de &face"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:128
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
-msgstr ""
+msgstr "Vue du dess&us"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:135
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
-msgstr ""
+msgstr "Vue latérale &gauche"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:142
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
-msgstr ""
+msgstr "Vue latérale &droite"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:149
msgctxt "@action:inmenu"
@@ -3959,7 +3984,7 @@ msgstr "Rechar&ger tous les modèles"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:edit"
msgid "Arrange All Models To All Build Plates"
-msgstr ""
+msgstr "Réorganiser tous les modèles sur tous les plateaux"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:358
msgctxt "@action:inmenu menubar:edit"
@@ -4019,7 +4044,7 @@ msgstr "Plug-ins installés..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:438
msgctxt "@action:inmenu menubar:view"
msgid "Expand/Collapse Sidebar"
-msgstr ""
+msgstr "Déplier / replier la barre latérale"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:26
msgctxt "@label:PrintjobStatus"
@@ -4054,12 +4079,12 @@ msgstr "Découpe indisponible"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Slice current printjob"
-msgstr ""
+msgstr "Découper la tâche d'impression en cours"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Cancel slicing process"
-msgstr ""
+msgstr "Annuler le processus de découpe"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:183
msgctxt "@label:Printjob"
@@ -4115,7 +4140,7 @@ msgstr "Enregistrer &sous..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:139
msgctxt "@title:menu menubar:file"
msgid "Save &Project..."
-msgstr ""
+msgstr "Enregistrer le &projet..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:162
msgctxt "@title:menu menubar:toplevel"
@@ -4192,7 +4217,7 @@ msgstr "Nouveau projet"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:555
msgctxt "@info:question"
msgid "Are you sure you want to start a new project? This will clear the build plate and any unsaved settings."
-msgstr "Êtes-vous sûr(e) de souhaiter lancer un nouveau projet ? Cela supprimera les objets du plateau ainsi que tous paramètres non enregistrés."
+msgstr "Êtes-vous sûr(e) de vouloir commencer un nouveau projet ? Cela supprimera les objets du plateau ainsi que tous paramètres non enregistrés."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:797
msgctxt "@window:title"
@@ -4217,7 +4242,7 @@ msgstr "Enregistrer le projet"
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:136
msgctxt "@action:label"
msgid "Extruder %1"
-msgstr "Extrudeuse %1"
+msgstr "Extrudeur %1"
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:146
msgctxt "@action:label"
@@ -4282,12 +4307,12 @@ msgstr "Générer les supports"
#: /home/ruben/Projects/Cura/resources/qml/SidebarSimple.qml:781
msgctxt "@label"
msgid "Generate structures to support parts of the model which have overhangs. Without these structures, such parts would collapse during printing."
-msgstr "Générer des structures pour soutenir les parties du modèle qui possèdent des porte-à-faux. Sans ces structures, ces parties s'effondreront durant l'impression."
+msgstr "Générer des supports pour soutenir les parties du modèle qui possèdent des porte-à-faux. Sans ces supports, ces parties s'effondreront durant l'impression."
#: /home/ruben/Projects/Cura/resources/qml/SidebarSimple.qml:799
msgctxt "@label"
msgid "Support Extruder"
-msgstr "Extrudeuse de soutien"
+msgstr "Extrudeur pour matériau support"
#: /home/ruben/Projects/Cura/resources/qml/SidebarSimple.qml:851
msgctxt "@label"
@@ -4302,7 +4327,7 @@ msgstr "Adhérence au plateau"
#: /home/ruben/Projects/Cura/resources/qml/SidebarSimple.qml:929
msgctxt "@label"
msgid "Enable printing a brim or raft. This will add a flat area around or under your object which is easy to cut off afterwards."
-msgstr "Activez l'impression d'une bordure ou plaquette (Brim/Raft). Cela ajoutera une zone plate autour de ou sous votre objet qui est facile à découper par la suite."
+msgstr "Activez l'impression du Brim ou Raft. Cela ajoutera une zone plate autour de ou sous votre objet qui est facile à retirer par la suite."
#: /home/ruben/Projects/Cura/resources/qml/SidebarSimple.qml:969
msgctxt "@label"
@@ -4354,7 +4379,7 @@ msgstr "Matériau"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:352
msgctxt "@label"
msgid "Check compatibility"
-msgstr ""
+msgstr "Vérifier la compatibilité"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:372
msgctxt "@tooltip"
@@ -4364,17 +4389,17 @@ msgstr "Cliquez ici pour vérifier la compatibilité des matériaux sur Ultimake
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:211
msgctxt "@option:check"
msgid "See only current build plate"
-msgstr ""
+msgstr "Afficher uniquement le plateau actuel"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:227
msgctxt "@action:button"
msgid "Arrange to all build plates"
-msgstr ""
+msgstr "Réorganiser sur tous les plateaux"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:247
msgctxt "@action:button"
msgid "Arrange current build plate"
-msgstr ""
+msgstr "Réorganiser le plateau actuel"
#: MachineSettingsAction/plugin.json
msgctxt "description"
@@ -4469,22 +4494,22 @@ msgstr "Impression par USB"
#: PrepareStage/plugin.json
msgctxt "description"
msgid "Provides a prepare stage in Cura."
-msgstr ""
+msgstr "Fournit une étape de préparation dans Cura."
#: PrepareStage/plugin.json
msgctxt "name"
msgid "Prepare Stage"
-msgstr ""
+msgstr "Étape de préparation"
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "description"
msgid "Provides an edit window for direct script editing."
-msgstr ""
+msgstr "Fournit une fenêtre d'édition pour l'édition directe de script."
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "name"
msgid "Live scripting tool"
-msgstr ""
+msgstr "Outil de scripting en direct"
#: RemovableDriveOutputDevice/plugin.json
msgctxt "description"
@@ -4509,12 +4534,12 @@ msgstr "Connexion au réseau UM3"
#: MonitorStage/plugin.json
msgctxt "description"
msgid "Provides a monitor stage in Cura."
-msgstr ""
+msgstr "Fournit une étape de surveillance dans Cura."
#: MonitorStage/plugin.json
msgctxt "name"
msgid "Monitor Stage"
-msgstr ""
+msgstr "Étape de surveillance"
#: FirmwareUpdateChecker/plugin.json
msgctxt "description"
@@ -4524,12 +4549,12 @@ msgstr "Vérifie les mises à jour du firmware."
#: FirmwareUpdateChecker/plugin.json
msgctxt "name"
msgid "Firmware Update Checker"
-msgstr "Vérificateur des mises à jour du firmware"
+msgstr "Vérification des mises à jour du firmware"
#: CuraSolidWorksPlugin/plugin.json
msgctxt "description"
msgid "Gives you the possibility to open certain files using SolidWorks itself. Conversion is done by this plugin and additional optimizations."
-msgstr ""
+msgstr "Donne la possibilité d'ouvrir certains fichiers via SolidWorks. La conversion est ensuite effectuée par ce plug-in et des optimisations supplémentaires."
#: CuraSolidWorksPlugin/plugin.json
msgctxt "name"
@@ -4584,7 +4609,7 @@ msgstr "Offre la possibilité de lire et d'écrire des profils matériels basés
#: XmlMaterialProfile/plugin.json
msgctxt "name"
msgid "Material Profiles"
-msgstr "Profils matériels"
+msgstr "Profils matériaux"
#: LegacyProfileReader/plugin.json
msgctxt "description"
@@ -4599,12 +4624,12 @@ msgstr "Lecteur de profil Cura antérieur"
#: CuraBlenderPlugin/plugin.json
msgctxt "description"
msgid "Helps to open Blender files directly in Cura."
-msgstr ""
+msgstr "Aide à ouvrir les fichiers Blender directement dans Cura."
#: CuraBlenderPlugin/plugin.json
msgctxt "name"
msgid "Blender Integration (experimental)"
-msgstr ""
+msgstr "Intégration Blender (expérimental)"
#: GCodeProfileReader/plugin.json
msgctxt "description"
@@ -4714,7 +4739,7 @@ msgstr "Vous aide à installer un bouton « exporter vers Cura » dans Siemens
#: cura-siemensnx-plugin/plugin.json
msgctxt "name"
msgid "Siemens NX Integration"
-msgstr "Siemens NX Integration"
+msgstr "Intégration Siemens NX"
#: 3MFReader/plugin.json
msgctxt "description"
@@ -4769,12 +4794,12 @@ msgstr "Générateur de profil Cura"
#: CuraPrintProfileCreator/plugin.json
msgctxt "description"
msgid "Allows material manufacturers to create new material and quality profiles using a drop-in UI."
-msgstr ""
+msgstr "Permet aux fabricants de matériaux de créer de nouveaux matériaux et profils de qualité à l'aide d'une interface utilisateur ad hoc."
#: CuraPrintProfileCreator/plugin.json
msgctxt "name"
msgid "Print Profile Assistant"
-msgstr ""
+msgstr "Assistant de profil d'impression"
#: 3MFWriter/plugin.json
msgctxt "description"
@@ -4789,17 +4814,17 @@ msgstr "Générateur 3MF"
#: UserAgreementPlugin/plugin.json
msgctxt "description"
msgid "Ask the user once if he/she agrees with our license"
-msgstr "Demander à l'utilisateur une fois s'il appose son accord à notre licence"
+msgstr "Demander à l'utilisateur une fois s'il est d'accord avec les termes de notre licence"
#: UserAgreementPlugin/plugin.json
msgctxt "name"
msgid "UserAgreement"
-msgstr "UserAgreement"
+msgstr "Accord de l'utilisateur"
#: UltimakerMachineActions/plugin.json
msgctxt "description"
msgid "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc)"
-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.)"
+msgstr "Fournit les actions de la machine pour les machines Ultimaker (tels que l'assistant de calibration du plateau, sélection des mises à niveau, etc.)"
#: UltimakerMachineActions/plugin.json
msgctxt "name"
diff --git a/resources/i18n/fr_FR/fdmprinter.def.json.po b/resources/i18n/fr_FR/fdmprinter.def.json.po
index e5aa684c90..35731b8312 100644
--- a/resources/i18n/fr_FR/fdmprinter.def.json.po
+++ b/resources/i18n/fr_FR/fdmprinter.def.json.po
@@ -1,20 +1,21 @@
-# Cura JSON setting files
-# Copyright (C) 2017 Ultimaker
+# Cura
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
-"PO-Revision-Date: 2017-11-30 13:05+0100\n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
+"PO-Revision-Date: 2018-02-13 15:31+0100\n"
"Last-Translator: Bothof \n"
"Language-Team: French\n"
"Language: fr_FR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.0.6\n"
#: fdmprinter.def.json
msgctxt "machine_settings label"
@@ -77,12 +78,12 @@ msgstr ""
#: fdmprinter.def.json
msgctxt "material_guid label"
msgid "Material GUID"
-msgstr "GUID matériau"
+msgstr "Identification GUID du matériau"
#: fdmprinter.def.json
msgctxt "material_guid description"
msgid "GUID of the material. This is set automatically. "
-msgstr "GUID du matériau. Cela est configuré automatiquement. "
+msgstr "Identification GUID du matériau. Cela est configuré automatiquement. "
#: fdmprinter.def.json
msgctxt "material_bed_temp_wait label"
@@ -152,7 +153,7 @@ msgstr "Forme du plateau"
#: fdmprinter.def.json
msgctxt "machine_shape description"
msgid "The shape of the build plate without taking unprintable areas into account."
-msgstr "La forme du plateau sans prendre les zones non imprimables en compte."
+msgstr "La forme du plateau sans prendre en compte les zones non imprimables."
#: fdmprinter.def.json
msgctxt "machine_shape option rectangular"
@@ -177,12 +178,12 @@ msgstr "La hauteur (sens Z) de la zone imprimable."
#: fdmprinter.def.json
msgctxt "machine_heated_bed label"
msgid "Has Heated Build Plate"
-msgstr "A un plateau chauffé"
+msgstr "A un plateau chauffant"
#: fdmprinter.def.json
msgctxt "machine_heated_bed description"
msgid "Whether the machine has a heated build plate present."
-msgstr "Si la machine a un plateau chauffé présent."
+msgstr "Si la machine a un plateau chauffant existant."
#: fdmprinter.def.json
msgctxt "machine_center_is_zero label"
@@ -202,7 +203,7 @@ msgstr "Nombre d'extrudeuses"
#: fdmprinter.def.json
msgctxt "machine_extruder_count description"
msgid "Number of extruder trains. An extruder train is the combination of a feeder, bowden tube, and nozzle."
-msgstr "Nombre de trains d'extrudeuse. Un train d'extrudeuse est la combinaison d'un chargeur, d'un tube bowden et d'une buse."
+msgstr "Nombre de systèmes d'extrusion. Un système d'extrusion est la combinaison d'un feeder, d'un tube bowden et d'une buse."
#: fdmprinter.def.json
msgctxt "machine_nozzle_tip_outer_diameter label"
@@ -242,7 +243,7 @@ msgstr "Longueur de la zone chauffée"
#: fdmprinter.def.json
msgctxt "machine_heat_zone_length description"
msgid "The distance from the tip of the nozzle in which heat from the nozzle is transferred to the filament."
-msgstr "Distance depuis la pointe du bec d'impression sur laquelle la chaleur du bec d'impression est transférée au filament."
+msgstr "Distance depuis la pointe de la buse sur laquelle la chaleur de la buse est transférée au filament."
#: fdmprinter.def.json
msgctxt "machine_filament_park_distance label"
@@ -252,7 +253,7 @@ msgstr "Distance de stationnement du filament"
#: fdmprinter.def.json
msgctxt "machine_filament_park_distance description"
msgid "The distance from the tip of the nozzle where to park the filament when an extruder is no longer used."
-msgstr "Distance depuis la pointe du bec sur laquelle stationner le filament lorsqu'une extrudeuse n'est plus utilisée."
+msgstr "Distance depuis la pointe de la buse sur laquelle stationne le filament lorsqu'une extrudeuse n'est plus utilisée."
#: fdmprinter.def.json
msgctxt "machine_nozzle_temp_enabled label"
@@ -267,7 +268,7 @@ msgstr "Contrôler ou non la température depuis Cura. Désactivez cette option
#: fdmprinter.def.json
msgctxt "machine_nozzle_heat_up_speed label"
msgid "Heat up speed"
-msgstr "Vitesse de chauffage"
+msgstr "Vitesse de chauffe"
#: fdmprinter.def.json
msgctxt "machine_nozzle_heat_up_speed description"
@@ -287,7 +288,7 @@ msgstr "La vitesse (°C/s) à laquelle la buse refroidit, sur une moyenne de la
#: fdmprinter.def.json
msgctxt "machine_min_cool_heat_time_window label"
msgid "Minimal Time Standby Temperature"
-msgstr "Durée minimale température de veille"
+msgstr "Température minimale de veille"
#: fdmprinter.def.json
msgctxt "machine_min_cool_heat_time_window description"
@@ -352,12 +353,12 @@ msgstr "Repetier"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract label"
msgid "Firmware Retraction"
-msgstr ""
+msgstr "Rétraction du firmware"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract description"
msgid "Whether to use firmware retract commands (G10/G11) instead of using the E property in G1 commands to retract the material."
-msgstr ""
+msgstr "S'il faut utiliser les commandes de rétraction du firmware (G10 / G11) au lieu d'utiliser la propriété E dans les commandes G1 pour rétracter le matériau."
#: fdmprinter.def.json
msgctxt "machine_disallowed_areas label"
@@ -372,12 +373,12 @@ msgstr "Une liste de polygones comportant les zones dans lesquelles la tête d'i
#: fdmprinter.def.json
msgctxt "nozzle_disallowed_areas label"
msgid "Nozzle Disallowed Areas"
-msgstr "Zones interdites au bec d'impression"
+msgstr "Zones interdites à la buse"
#: fdmprinter.def.json
msgctxt "nozzle_disallowed_areas description"
msgid "A list of polygons with areas the nozzle is not allowed to enter."
-msgstr "Une liste de polygones comportant les zones dans lesquelles le bec n'a pas le droit de pénétrer."
+msgstr "Une liste de polygones comportant les zones dans lesquelles la buse n'a pas le droit de pénétrer."
#: fdmprinter.def.json
msgctxt "machine_head_polygon label"
@@ -387,7 +388,7 @@ msgstr "Polygone de la tête de machine"
#: fdmprinter.def.json
msgctxt "machine_head_polygon description"
msgid "A 2D silhouette of the print head (fan caps excluded)."
-msgstr "Une silhouette 2D de la tête d'impression (sans les capuchons du ventilateur)."
+msgstr "Une silhouette 2D de la tête d'impression (sans les carter du ventilateur)."
#: fdmprinter.def.json
msgctxt "machine_head_with_fans_polygon label"
@@ -397,7 +398,7 @@ msgstr "Tête de la machine et polygone du ventilateur"
#: fdmprinter.def.json
msgctxt "machine_head_with_fans_polygon description"
msgid "A 2D silhouette of the print head (fan caps included)."
-msgstr "Une silhouette 2D de la tête d'impression (avec les capuchons du ventilateur)."
+msgstr "Une silhouette 2D de la tête d'impression (avec les carters du ventilateur)."
#: fdmprinter.def.json
msgctxt "gantry_height label"
@@ -412,12 +413,12 @@ msgstr "La différence de hauteur entre la pointe de la buse et le système de p
#: fdmprinter.def.json
msgctxt "machine_nozzle_id label"
msgid "Nozzle ID"
-msgstr "ID buse"
+msgstr "ID de la buse"
#: fdmprinter.def.json
msgctxt "machine_nozzle_id description"
msgid "The nozzle ID for an extruder train, such as \"AA 0.4\" and \"BB 0.8\"."
-msgstr "ID buse pour un train d'extrudeuse, comme « AA 0.4 » et « BB 0.8 »."
+msgstr "ID de la buse pour un système d'extrusion, comme « AA 0.4 » et « BB 0.8 »."
#: fdmprinter.def.json
msgctxt "machine_nozzle_size label"
@@ -432,17 +433,17 @@ msgstr "Le diamètre intérieur de la buse. Modifiez ce paramètre si vous utili
#: fdmprinter.def.json
msgctxt "machine_use_extruder_offset_to_offset_coords label"
msgid "Offset With Extruder"
-msgstr "Décalage avec extrudeuse"
+msgstr "Offset avec extrudeuse"
#: fdmprinter.def.json
msgctxt "machine_use_extruder_offset_to_offset_coords description"
msgid "Apply the extruder offset to the coordinate system."
-msgstr "Appliquer le décalage de l'extrudeuse au système de coordonnées."
+msgstr "Appliquer l'offset 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 "Position d'amorçage en Z de l'extrudeuse"
#: fdmprinter.def.json
msgctxt "extruder_prime_pos_z description"
@@ -602,7 +603,7 @@ msgstr "Tous les paramètres qui influent sur la résolution de l'impression. Ce
#: fdmprinter.def.json
msgctxt "layer_height label"
msgid "Layer Height"
-msgstr "Hauteur de la couche"
+msgstr "Hauteur de couche"
#: fdmprinter.def.json
msgctxt "layer_height description"
@@ -612,7 +613,7 @@ msgstr "La hauteur de chaque couche en mm. Des valeurs plus élevées créent de
#: fdmprinter.def.json
msgctxt "layer_height_0 label"
msgid "Initial Layer Height"
-msgstr "Hauteur de la couche initiale"
+msgstr "Hauteur de couche initiale"
#: fdmprinter.def.json
msgctxt "layer_height_0 description"
@@ -632,12 +633,12 @@ msgstr "Largeur d'une ligne. Généralement, la largeur de chaque ligne doit cor
#: fdmprinter.def.json
msgctxt "wall_line_width label"
msgid "Wall Line Width"
-msgstr "Largeur de ligne de la paroi"
+msgstr "Largeur de ligne de la coque"
#: fdmprinter.def.json
msgctxt "wall_line_width description"
msgid "Width of a single wall line."
-msgstr "Largeur d'une seule ligne de la paroi."
+msgstr "Largeur d'une seule ligne de la paroie."
#: fdmprinter.def.json
msgctxt "wall_line_width_0 label"
@@ -692,7 +693,7 @@ msgstr "Largeur d'une seule ligne de jupe ou de bordure."
#: fdmprinter.def.json
msgctxt "support_line_width label"
msgid "Support Line Width"
-msgstr "Largeur de ligne de support"
+msgstr "Largeur de ligne des supports"
#: fdmprinter.def.json
msgctxt "support_line_width description"
@@ -712,12 +713,12 @@ msgstr "Largeur d'une seule ligne de plafond ou de bas de support."
#: fdmprinter.def.json
msgctxt "support_roof_line_width label"
msgid "Support Roof Line Width"
-msgstr "Largeur de ligne de plafond de support"
+msgstr "Largeur de ligne du toit de support"
#: fdmprinter.def.json
msgctxt "support_roof_line_width description"
msgid "Width of a single support roof line."
-msgstr "Largeur d'une seule ligne de plafond de support."
+msgstr "Largeur d'une seule ligne de toit de support."
#: fdmprinter.def.json
msgctxt "support_bottom_line_width label"
@@ -767,7 +768,7 @@ msgstr "Extrudeuse de paroi"
#: fdmprinter.def.json
msgctxt "wall_extruder_nr description"
msgid "The extruder train used for printing the walls. This is used in multi-extrusion."
-msgstr "Le train d'extrudeuse utilisé pour l'impression des parois. Cela est utilisé en multi-extrusion."
+msgstr "Le système d'extrusion utilisé pour l'impression des parois. Cela est utilisé en multi-extrusion."
#: fdmprinter.def.json
msgctxt "wall_0_extruder_nr label"
@@ -777,7 +778,7 @@ msgstr "Extrudeuse de paroi externe"
#: fdmprinter.def.json
msgctxt "wall_0_extruder_nr description"
msgid "The extruder train used for printing the outer wall. This is used in multi-extrusion."
-msgstr "Le train d'extrudeuse utilisé pour l'impression des parois externes. Cela est utilisé en multi-extrusion."
+msgstr "Le système d'extrusion utilisé pour l'impression des parois externes. Cela est utilisé en multi-extrusion."
#: fdmprinter.def.json
msgctxt "wall_x_extruder_nr label"
@@ -787,7 +788,7 @@ msgstr "Extrudeuse de paroi interne"
#: fdmprinter.def.json
msgctxt "wall_x_extruder_nr description"
msgid "The extruder train used for printing the inner walls. This is used in multi-extrusion."
-msgstr "Le train d'extrudeuse utilisé pour l'impression des parois internes. Cela est utilisé en multi-extrusion."
+msgstr "Le système d'extrusion utilisé pour l'impression des parois internes. Cela est utilisé en multi-extrusion."
#: fdmprinter.def.json
msgctxt "wall_thickness label"
@@ -827,7 +828,7 @@ msgstr "Extrudeuse de couche extérieure de la surface supérieure"
#: fdmprinter.def.json
msgctxt "roofing_extruder_nr description"
msgid "The extruder train used for printing the top most skin. This is used in multi-extrusion."
-msgstr "Le train d'extrudeuse utilisé pour l'impression de la couche extérieure supérieure. Cela est utilisé en multi-extrusion."
+msgstr "Le système d'extrusion utilisé pour l'impression de la couche extérieure supérieure. Cela est utilisé en multi-extrusion."
#: fdmprinter.def.json
msgctxt "roofing_layer_count label"
@@ -847,7 +848,7 @@ msgstr "Extrudeuse du dessus/dessous"
#: fdmprinter.def.json
msgctxt "top_bottom_extruder_nr description"
msgid "The extruder train used for printing the top and bottom skin. This is used in multi-extrusion."
-msgstr "Le train d'extrudeuse utilisé pour l'impression de la couche extérieure du haut et du bas. Cela est utilisé en multi-extrusion."
+msgstr "Le système d'extrusion utilisé pour l'impression de la couche extérieure du haut et du bas. Cela est utilisé en multi-extrusion."
#: fdmprinter.def.json
msgctxt "top_bottom_thickness label"
@@ -962,12 +963,12 @@ msgstr "Une liste de sens de ligne (exprimés en nombres entiers) à utiliser lo
#: fdmprinter.def.json
msgctxt "wall_0_inset label"
msgid "Outer Wall Inset"
-msgstr "Insert de paroi externe"
+msgstr "Enchevêtrement de la paroi externe"
#: fdmprinter.def.json
msgctxt "wall_0_inset description"
msgid "Inset applied to the path of the outer wall. If the outer wall is smaller than the nozzle, and printed after the inner walls, use this offset to get the hole in the nozzle to overlap with the inner walls instead of the outside of the model."
-msgstr "Insert appliqué sur le passage de la paroi externe. Si la paroi externe est plus petite que la buse et imprimée après les parois intérieures, utiliser ce décalage pour que le trou dans la buse chevauche les parois internes et non l'extérieur du modèle."
+msgstr "Enchevêtrement appliqué sur le passage de la paroi externe. Si la paroi externe est plus petite que la buse et imprimée après les parois intérieures, utiliser cet Offset pour que le trou dans la buse chevauche les parois internes et non l'extérieur du modèle."
#: fdmprinter.def.json
msgctxt "optimize_wall_printing_order label"
@@ -1032,12 +1033,12 @@ msgstr "Compenser le débit pour les parties d'une paroi intérieure imprimées
#: fdmprinter.def.json
msgctxt "fill_perimeter_gaps label"
msgid "Fill Gaps Between Walls"
-msgstr "Remplir les trous entre les parois"
+msgstr "Remplir l'espace entre les parois"
#: fdmprinter.def.json
msgctxt "fill_perimeter_gaps description"
msgid "Fills the gaps between walls where no walls fit."
-msgstr "Imprime les remplissages entre les parois lorsqu'aucune paroi ne convient."
+msgstr "Rempli l'espace entre les parois lorsqu'aucune paroi ne convient."
#: fdmprinter.def.json
msgctxt "fill_perimeter_gaps option nowhere"
@@ -1052,12 +1053,12 @@ msgstr "Partout"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps label"
msgid "Filter Out Tiny Gaps"
-msgstr ""
+msgstr "Filtrer les petits espaces"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps description"
msgid "Filter out tiny gaps to reduce blobs on outside of model."
-msgstr ""
+msgstr "Filtrer les petits espaces pour réduire la présence de gouttes à l'extérieur du modèle."
#: fdmprinter.def.json
msgctxt "fill_outline_gaps label"
@@ -1072,12 +1073,12 @@ msgstr "Imprimer les parties du modèle qui sont horizontalement plus fines que
#: fdmprinter.def.json
msgctxt "xy_offset label"
msgid "Horizontal Expansion"
-msgstr "Vitesse d’impression horizontale"
+msgstr "Expansion horizontale"
#: fdmprinter.def.json
msgctxt "xy_offset description"
msgid "Amount of offset applied to all polygons in each layer. Positive values can compensate for too big holes; negative values can compensate for too small holes."
-msgstr "Le décalage appliqué à tous les polygones dans chaque couche. Une valeur positive peut compenser les trous trop gros ; une valeur négative peut compenser les trous trop petits."
+msgstr "L'offset appliqué à tous les polygones dans chaque couche. Une valeur positive peut compenser les trous trop gros ; une valeur négative peut compenser les trous trop petits."
#: fdmprinter.def.json
msgctxt "xy_offset_layer_0 label"
@@ -1087,7 +1088,7 @@ msgstr "Expansion horizontale de la couche initiale"
#: fdmprinter.def.json
msgctxt "xy_offset_layer_0 description"
msgid "Amount of offset applied to all polygons in the first layer. A negative value can compensate for squishing of the first layer known as \"elephant's foot\"."
-msgstr "Le décalage appliqué à tous les polygones dans la première couche. Une valeur négative peut compenser l'écrasement de la première couche, appelé « patte d'éléphant »."
+msgstr "L'offset appliqué à tous les polygones dans la première couche. Une valeur négative peut compenser l'écrasement de la première couche, appelé « patte d'éléphant »."
#: fdmprinter.def.json
msgctxt "z_seam_type label"
@@ -1097,7 +1098,7 @@ msgstr "Alignement de la jointure en Z"
#: fdmprinter.def.json
msgctxt "z_seam_type description"
msgid "Starting point of each path in a layer. When paths in consecutive layers start at the same point a vertical seam may show on the print. When aligning these near a user specified location, the seam is easiest to remove. When placed randomly the inaccuracies at the paths' start will be less noticeable. When taking the shortest path the print will be quicker."
-msgstr "Point de départ de chaque voie dans une couche. Quand les voies dans les couches consécutives démarrent au même endroit, une jointure verticale peut apparaître sur l'impression. En alignant les points de départ près d'un emplacement défini par l'utilisateur, la jointure sera plus facile à faire disparaître. Lorsqu'elles sont disposées de manière aléatoire, les imprécisions de départ des voies seront moins visibles. En choisissant la voie la plus courte, l'impression se fera plus rapidement."
+msgstr "Point de départ de chaque chemin dans une couche. Quand les chemins dans les couches consécutives démarrent au même endroit, une jointure verticale peut apparaître sur l'impression. En alignant les points de départ près d'un emplacement défini par l'utilisateur, la jointure sera plus facile à faire disparaître. Lorsqu'elles sont disposées de manière aléatoire, les imprécisions de départ des chemins seront moins visibles. En choisissant le chemin le plus court, l'impression se fera plus rapidement."
#: fdmprinter.def.json
msgctxt "z_seam_type option back"
@@ -1182,12 +1183,12 @@ msgstr "Si cette option est activée, les coordonnées de la jointure z sont rel
#: fdmprinter.def.json
msgctxt "skin_no_small_gaps_heuristic label"
msgid "Ignore Small Z Gaps"
-msgstr "Ignorer les petits trous en Z"
+msgstr "Ignorer les petits espaces en Z"
#: fdmprinter.def.json
msgctxt "skin_no_small_gaps_heuristic description"
msgid "When the model has small vertical gaps, about 5% extra computation time can be spent on generating top and bottom skin in these narrow spaces. In such case, disable the setting."
-msgstr "Quand le modèle présente de petits trous verticaux, environ 5 % de temps de calcul supplémentaire peut être alloué à la génération de couches du dessus et du dessous dans ces espaces étroits. Dans ce cas, désactivez ce paramètre."
+msgstr "Quand le modèle présente de petits espaces verticaux, environ 5 % de temps de calcul supplémentaire peut être alloué à la génération de couches du dessus et du dessous dans ces espaces étroits. Dans ce cas, désactivez ce paramètre."
#: fdmprinter.def.json
msgctxt "skin_outline_count label"
@@ -1202,32 +1203,32 @@ msgstr "Remplace la partie la plus externe du motif du dessus/dessous par un cer
#: fdmprinter.def.json
msgctxt "ironing_enabled label"
msgid "Enable Ironing"
-msgstr "Activer l'étirage"
+msgstr "Activer le lissage"
#: fdmprinter.def.json
msgctxt "ironing_enabled description"
msgid "Go over the top surface one additional time, but without extruding material. This is meant to melt the plastic on top further, creating a smoother surface."
-msgstr "Aller au-dessus de la surface supérieure une fois supplémentaire, mais sans extruder de matériau. Cela signifie de faire fondre le plastique en haut un peu plus, pour créer une surface lisse."
+msgstr "Aller au-dessus de la surface supérieure une fois supplémentaire, mais sans extruder de matériau. Cela signifie de faire fondre le plastique sur les couches supérieures, pour créer une surface lisse."
#: fdmprinter.def.json
msgctxt "ironing_only_highest_layer label"
msgid "Iron Only Highest Layer"
-msgstr "N'étirer que la couche supérieure"
+msgstr "Ne lisser que la couche supérieure"
#: fdmprinter.def.json
msgctxt "ironing_only_highest_layer description"
msgid "Only perform ironing on the very last layer of the mesh. This saves time if the lower layers don't need a smooth surface finish."
-msgstr "N'exécute un étirage que sur l'ultime couche du maillage. Ceci économise du temps si les couches inférieures ne nécessitent pas de fini lisse de surface."
+msgstr "N'exécute un lissage que sur la dernière couche du maillage. Ceci économise du temps si les couches inférieures ne nécessitent pas de finition lissée."
#: fdmprinter.def.json
msgctxt "ironing_pattern label"
msgid "Ironing Pattern"
-msgstr "Motif d'étirage"
+msgstr "Motif de lissage"
#: fdmprinter.def.json
msgctxt "ironing_pattern description"
msgid "The pattern to use for ironing top surfaces."
-msgstr "Le motif à utiliser pour étirer les surfaces supérieures."
+msgstr "Le motif à utiliser pour lisser les surfaces supérieures."
#: fdmprinter.def.json
msgctxt "ironing_pattern option concentric"
@@ -1242,37 +1243,37 @@ msgstr "Zig Zag"
#: fdmprinter.def.json
msgctxt "ironing_line_spacing label"
msgid "Ironing Line Spacing"
-msgstr "Interligne de l'étirage"
+msgstr "Interligne de lissage"
#: fdmprinter.def.json
msgctxt "ironing_line_spacing description"
msgid "The distance between the lines of ironing."
-msgstr "La distance entre les lignes d'étirage."
+msgstr "La distance entre les lignes de lissage"
#: fdmprinter.def.json
msgctxt "ironing_flow label"
msgid "Ironing Flow"
-msgstr "Flux d'étirage"
+msgstr "Flux de lissage"
#: fdmprinter.def.json
msgctxt "ironing_flow description"
msgid "The amount of material, relative to a normal skin line, to extrude during ironing. Keeping the nozzle filled helps filling some of the crevices of the top surface, but too much results in overextrusion and blips on the side of the surface."
-msgstr "La quantité de matériau, relative à une ligne de couche extérieure normale, à extruder pendant l'étirage. Le fait de garder la buse pleine aide à remplir certaines des crevasses de la surface supérieure ; mais si la quantité est trop importante, cela entraînera une surextrusion et l'apparition de coupures sur le côté de la surface."
+msgstr "La quantité de matériau, relative à une ligne de couche extérieure normale, à extruder pendant le lissage. Le fait de garder la buse pleine aide à remplir certaines des crevasses de la surface supérieure ; mais si la quantité est trop importante, cela entraînera une surextrusion et l'apparition de coupures sur le côté de la surface."
#: fdmprinter.def.json
msgctxt "ironing_inset label"
msgid "Ironing Inset"
-msgstr "Insert d'étirage"
+msgstr "Chevauchement du lissage"
#: fdmprinter.def.json
msgctxt "ironing_inset description"
msgid "A distance to keep from the edges of the model. Ironing all the way to the edge of the mesh may result in a jagged edge on your print."
-msgstr "Distance à garder à partir des bords du modèle. Étirer jusqu'au bord de la maille peut entraîner l'apparition d'un bord denté sur votre impression."
+msgstr "Distance à garder à partir des bords du modèle. Lisser jusqu'au bord de la maille peut entraîner l'apparition d'un bord denté sur votre impression."
#: fdmprinter.def.json
msgctxt "speed_ironing label"
msgid "Ironing Speed"
-msgstr "Vitesse d'étirage"
+msgstr "Vitesse de lissage"
#: fdmprinter.def.json
msgctxt "speed_ironing description"
@@ -1282,22 +1283,22 @@ msgstr "La vitesse à laquelle passer sur la surface supérieure."
#: fdmprinter.def.json
msgctxt "acceleration_ironing label"
msgid "Ironing Acceleration"
-msgstr "Accélération d'étirage"
+msgstr "Accélération du lissage"
#: fdmprinter.def.json
msgctxt "acceleration_ironing description"
msgid "The acceleration with which ironing is performed."
-msgstr "L'accélération selon laquelle l'étirage est effectué."
+msgstr "L'accélération selon laquelle le lissage est effectué."
#: fdmprinter.def.json
msgctxt "jerk_ironing label"
msgid "Ironing Jerk"
-msgstr "Saccade d'étirage"
+msgstr "Saccade du lissage"
#: fdmprinter.def.json
msgctxt "jerk_ironing description"
msgid "The maximum instantaneous velocity change while performing ironing."
-msgstr "Le changement instantané maximal de vitesse lors de l'étirage."
+msgstr "Le changement instantané maximal de vitesse lors du lissage."
#: fdmprinter.def.json
msgctxt "infill label"
@@ -1317,7 +1318,7 @@ msgstr "Extrudeuse de remplissage"
#: fdmprinter.def.json
msgctxt "infill_extruder_nr description"
msgid "The extruder train used for printing infill. This is used in multi-extrusion."
-msgstr "Le train d'extrudeuse utilisé pour l'impression du remplissage. Cela est utilisé en multi-extrusion."
+msgstr "Le système d'extrusion utilisé pour l'impression du remplissage. Cela est utilisé en multi-extrusion."
#: fdmprinter.def.json
msgctxt "infill_sparse_density label"
@@ -1342,12 +1343,12 @@ msgstr "Distance entre les lignes de remplissage imprimées. Ce paramètre est c
#: fdmprinter.def.json
msgctxt "infill_pattern label"
msgid "Infill Pattern"
-msgstr "Motif de remplissage"
+msgstr "Motif du remplissage"
#: fdmprinter.def.json
msgctxt "infill_pattern description"
msgid "The pattern of the infill material of the print. The line and zig zag infill swap direction on alternate layers, reducing material cost. The grid, triangle, tri-hexagon, cubic, octet, quarter cubic, cross and concentric patterns are fully printed every layer. Cubic, quarter cubic and octet infill change with every layer to provide a more equal distribution of strength over each direction."
-msgstr "Motif du matériau de remplissage de l'impression. La ligne et le remplissage en zigzag changent de sens à chaque alternance de couche, réduisant ainsi les coûts matériels. Les motifs en grille, en triangle, trihexagonaux, cubiques, octaédriques, quart cubiques et concentriques sont entièrement imprimés sur chaque couche. Les remplissages cubique, quart cubique et octaédrique changent à chaque couche afin d'offrir une répartition plus égale de la solidité dans chaque direction."
+msgstr "Le motif du remplissage de l'impression. La ligne et le remplissage en zigzag changent de sens à chaque alternance de couche, réduisant ainsi les coûts matériels. Les motifs en grille, en triangle, trihexagonaux, cubiques, octaédriques, quart cubiques et concentriques sont entièrement imprimés sur chaque couche. Les remplissages cubique, quart cubique et octaédrique changent à chaque couche afin d'offrir une répartition plus égale de la solidité dans chaque direction."
#: fdmprinter.def.json
msgctxt "infill_pattern option grid"
@@ -1437,22 +1438,22 @@ msgstr "Une liste de sens de ligne (exprimés en nombres entiers) à utiliser. L
#: fdmprinter.def.json
msgctxt "infill_offset_x label"
msgid "Infill X Offset"
-msgstr "Remplissage Décalage X"
+msgstr "Offset Remplissage X"
#: fdmprinter.def.json
msgctxt "infill_offset_x description"
msgid "The infill pattern is moved this distance along the X axis."
-msgstr ""
+msgstr "Le motif de remplissage est décalé de cette distance sur l'axe X."
#: fdmprinter.def.json
msgctxt "infill_offset_y label"
msgid "Infill Y Offset"
-msgstr "Remplissage Décalage Y"
+msgstr "Remplissage Offset Y"
#: fdmprinter.def.json
msgctxt "infill_offset_y description"
msgid "The infill pattern is moved this distance along the Y axis."
-msgstr ""
+msgstr "Le motif de remplissage est décalé de cette distance sur l'axe Y."
#: fdmprinter.def.json
msgctxt "sub_div_rad_add label"
@@ -1472,7 +1473,7 @@ msgstr "Pourcentage de chevauchement du remplissage"
#: fdmprinter.def.json
msgctxt "infill_overlap description"
msgid "The amount of overlap between the infill and the walls as a percentage of the infill line width. A slight overlap allows the walls to connect firmly to the infill."
-msgstr ""
+msgstr "Le degré de chevauchement entre le remplissage et les parois exprimé en pourcentage de la largeur de ligne de remplissage. Un chevauchement faible permet aux parois de se connecter fermement au remplissage."
#: fdmprinter.def.json
msgctxt "infill_overlap_mm label"
@@ -1492,7 +1493,7 @@ msgstr "Pourcentage de chevauchement de la couche extérieure"
#: fdmprinter.def.json
msgctxt "skin_overlap description"
msgid "The amount of overlap between the skin and the walls as a percentage of the skin line width. A slight overlap allows the walls to connect firmly to the skin. This is a percentage of the average line widths of the skin lines and the innermost wall."
-msgstr ""
+msgstr "Le montant de chevauchement entre la couche extérieure et les parois en pourcentage de la largeur de ligne de couche extérieure. Un chevauchement faible permet aux parois de se connecter fermement à la couche extérieure. Ce montant est un pourcentage des largeurs moyennes des lignes de la couche extérieure et de la paroi la plus intérieure."
#: fdmprinter.def.json
msgctxt "skin_overlap_mm label"
@@ -1662,7 +1663,7 @@ msgstr "Température d’impression par défaut"
#: fdmprinter.def.json
msgctxt "default_material_print_temperature description"
msgid "The default temperature used for printing. This should be the \"base\" temperature of a material. All other print temperatures should use offsets based on this value"
-msgstr "La température par défaut utilisée pour l'impression. Il doit s'agir de la température de « base » d'un matériau. Toutes les autres températures d'impression doivent utiliser des décalages basés sur cette valeur."
+msgstr "La température par défaut utilisée pour l'impression. Il doit s'agir de la température de « base » d'un matériau. Toutes les autres températures d'impression doivent utiliser des offset basés sur cette valeur."
#: fdmprinter.def.json
msgctxt "material_print_temperature label"
@@ -1722,7 +1723,7 @@ msgstr "Température du plateau"
#: fdmprinter.def.json
msgctxt "material_bed_temperature description"
msgid "The temperature used for the heated build plate. If this is 0, the bed temperature will not be adjusted."
-msgstr ""
+msgstr "Température utilisée pour le plateau chauffant. Si elle est définie sur 0, la température du plateau ne sera pas ajustée."
#: fdmprinter.def.json
msgctxt "material_bed_temperature_layer_0 label"
@@ -1792,7 +1793,7 @@ msgstr "Rétracter au changement de couche"
#: fdmprinter.def.json
msgctxt "retract_at_layer_change description"
msgid "Retract the filament when the nozzle is moving to the next layer."
-msgstr "Rétracter le filament quand le bec se déplace vers la prochaine couche. "
+msgstr "Rétracter le filament quand la buse se déplace vers la prochaine couche."
#: fdmprinter.def.json
msgctxt "retraction_amount label"
@@ -2547,12 +2548,12 @@ msgstr "déplacement"
#: fdmprinter.def.json
msgctxt "retraction_combing label"
msgid "Combing Mode"
-msgstr "Mode de détours"
+msgstr "Mode detour"
#: fdmprinter.def.json
msgctxt "retraction_combing description"
msgid "Combing keeps the nozzle within already printed areas when traveling. This results in slightly longer travel moves but reduces the need for retractions. If combing is off, the material will retract and the nozzle moves in a straight line to the next point. It is also possible to avoid combing over top/bottom skin areas by combing within the infill only."
-msgstr "Les détours (le 'combing') maintiennent le bec dans les zones déjà imprimées lors des déplacements. Cela résulte en des déplacements légèrement plus longs mais réduit le recours aux rétractions. Si les détours sont désactivés, le matériau se rétractera et le bec se déplacera en ligne droite jusqu'au point suivant. Il est également possible d'éviter les détours sur les zones de la couche du dessus / dessous en effectuant les détours uniquement dans le remplissage."
+msgstr "Les détours (le 'combing') maintiennent la buse dans les zones déjà imprimées lors des déplacements. Cela résulte en des déplacements légèrement plus longs mais réduit le recours aux rétractions. Si les détours sont désactivés, le matériau se rétractera et la buze se déplacera en ligne droite jusqu'au point suivant. Il est également possible d'éviter les détours sur les zones de la couche du dessus / dessous en effectuant les détours uniquement dans le remplissage."
#: fdmprinter.def.json
msgctxt "retraction_combing option off"
@@ -2732,7 +2733,7 @@ msgstr "La durée de couche qui définit la limite entre la vitesse régulière
#: fdmprinter.def.json
msgctxt "cool_fan_speed_0 label"
msgid "Initial Fan Speed"
-msgstr "Vitesse des ventilateurs initiale"
+msgstr "Vitesse initiale des ventilateurs"
#: fdmprinter.def.json
msgctxt "cool_fan_speed_0 description"
@@ -2817,7 +2818,7 @@ msgstr "Extrudeuse de support"
#: fdmprinter.def.json
msgctxt "support_extruder_nr description"
msgid "The extruder train to use for printing the support. This is used in multi-extrusion."
-msgstr "Le train d'extrudeuse à utiliser pour l'impression du support. Cela est utilisé en multi-extrusion."
+msgstr "Le système d'extrusion à utiliser pour l'impression du support. Cela est utilisé en multi-extrusion."
#: fdmprinter.def.json
msgctxt "support_infill_extruder_nr label"
@@ -2827,7 +2828,7 @@ msgstr "Extrudeuse de remplissage du support"
#: fdmprinter.def.json
msgctxt "support_infill_extruder_nr description"
msgid "The extruder train to use for printing the infill of the support. This is used in multi-extrusion."
-msgstr "Le train d'extrudeuse à utiliser pour l'impression du remplissage du support. Cela est utilisé en multi-extrusion."
+msgstr "Le système d'extrusion à utiliser pour l'impression du remplissage du support. Cela est utilisé en multi-extrusion."
#: fdmprinter.def.json
msgctxt "support_extruder_nr_layer_0 label"
@@ -2837,7 +2838,7 @@ msgstr "Extrudeuse de support de la première couche"
#: fdmprinter.def.json
msgctxt "support_extruder_nr_layer_0 description"
msgid "The extruder train to use for printing the first layer of support infill. This is used in multi-extrusion."
-msgstr "Le train d'extrudeuse à utiliser pour l'impression de la première couche de remplissage du support. Cela est utilisé en multi-extrusion."
+msgstr "Le système d'extrusion à utiliser pour l'impression de la première couche de remplissage du support. Cela est utilisé en multi-extrusion."
#: fdmprinter.def.json
msgctxt "support_interface_extruder_nr label"
@@ -2847,7 +2848,7 @@ msgstr "Extrudeuse de l'interface du support"
#: fdmprinter.def.json
msgctxt "support_interface_extruder_nr description"
msgid "The extruder train to use for printing the roofs and floors of the support. This is used in multi-extrusion."
-msgstr "Le train d'extrudeuse à utiliser pour l'impression des plafonds et bas du support. Cela est utilisé en multi-extrusion."
+msgstr "Le système d'extrusion à utiliser pour l'impression des plafonds et bas du support. Cela est utilisé en multi-extrusion."
#: fdmprinter.def.json
msgctxt "support_roof_extruder_nr label"
@@ -2857,7 +2858,7 @@ msgstr "Extrudeuse des plafonds de support"
#: fdmprinter.def.json
msgctxt "support_roof_extruder_nr description"
msgid "The extruder train to use for printing the roofs of the support. This is used in multi-extrusion."
-msgstr "Le train d'extrudeuse à utiliser pour l'impression des plafonds du support. Cela est utilisé en multi-extrusion."
+msgstr "Le système d'extrusion à utiliser pour l'impression des plafonds du support. Cela est utilisé en multi-extrusion."
#: fdmprinter.def.json
msgctxt "support_bottom_extruder_nr label"
@@ -2867,7 +2868,7 @@ msgstr "Extrudeuse des bas de support"
#: fdmprinter.def.json
msgctxt "support_bottom_extruder_nr description"
msgid "The extruder train to use for printing the floors of the support. This is used in multi-extrusion."
-msgstr "Le train d'extrudeuse à utiliser pour l'impression des bas du support. Cela est utilisé en multi-extrusion."
+msgstr "Le système d'extrusion à utiliser pour l'impression des bas du support. Cela est utilisé en multi-extrusion."
#: fdmprinter.def.json
msgctxt "support_type label"
@@ -3082,7 +3083,7 @@ msgstr "Expansion horizontale des supports"
#: fdmprinter.def.json
msgctxt "support_offset description"
msgid "Amount of offset applied to all support polygons in each layer. Positive values can smooth out the support areas and result in more sturdy support."
-msgstr "Le décalage appliqué à tous les polygones pour chaque couche. Une valeur positive peut lisser les zones de support et rendre le support plus solide."
+msgstr "L'offset appliqué à tous les polygones pour chaque couche. Une valeur positive peut lisser les zones de support et rendre le support plus solide."
#: fdmprinter.def.json
msgctxt "support_infill_sparse_thickness label"
@@ -3482,7 +3483,7 @@ 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 de la jupe/la bordure/du radeau. Cela est utilisé en multi-extrusion."
+msgstr "Le système d'extrusion à 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"
@@ -3931,7 +3932,7 @@ msgstr "Compensation du débit : la quantité de matériau extrudée est multip
#: fdmprinter.def.json
msgctxt "prime_tower_wipe_enabled label"
msgid "Wipe Inactive Nozzle on Prime Tower"
-msgstr "Essuyer le bec d'impression inactif sur la tour primaire"
+msgstr "Essuyer la buse d'impression inactif sur la tour primaire"
#: fdmprinter.def.json
msgctxt "prime_tower_wipe_enabled description"
@@ -4266,82 +4267,82 @@ msgstr "expérimental !"
#: fdmprinter.def.json
msgctxt "support_tree_enable label"
msgid "Tree Support"
-msgstr ""
+msgstr "Support arborescent"
#: fdmprinter.def.json
msgctxt "support_tree_enable description"
msgid "Generate a tree-like support with branches that support your print. This may reduce material usage and print time, but greatly increases slicing time."
-msgstr ""
+msgstr "Générer un support arborescent avec des branches qui soutiennent votre impression. Cela peut réduire l'utilisation de matériau et le temps d'impression, mais augmente considérablement le temps de découpage."
#: fdmprinter.def.json
msgctxt "support_tree_angle label"
msgid "Tree Support Branch Angle"
-msgstr ""
+msgstr "Angle des branches de support arborescent"
#: fdmprinter.def.json
msgctxt "support_tree_angle description"
msgid "The angle of the branches. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach."
-msgstr ""
+msgstr "Angle des branches. Utilisez un angle plus faible pour les rendre plus verticales et plus stables ; utilisez un angle plus élevé pour avoir plus de portée."
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance label"
msgid "Tree Support Branch Distance"
-msgstr ""
+msgstr "Distance des branches de support arborescent"
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance description"
msgid "How far apart the branches need to be when they touch the model. Making this distance small will cause the tree support to touch the model at more points, causing better overhang but making support harder to remove."
-msgstr ""
+msgstr "Distance à laquelle doivent se trouver les branches lorsqu'elles touchent le modèle. Si vous réduisez cette distance, le support arborescent touchera le modèle à plus d'endroits, ce qui causera un meilleur porte-à-faux mais rendra le support plus difficile à enlever."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter label"
msgid "Tree Support Branch Diameter"
-msgstr ""
+msgstr "Diamètre des branches de support arborescent"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter description"
msgid "The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this."
-msgstr ""
+msgstr "Diamètre des branches les plus minces du support arborescent. Plus les branches sont épaisses, plus elles sont robustes ; les branches proches de la base seront plus épaisses que cette valeur."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle label"
msgid "Tree Support Branch Diameter Angle"
-msgstr ""
+msgstr "Angle de diamètre des branches de support arborescent"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle description"
msgid "The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the tree support."
-msgstr ""
+msgstr "Angle du diamètre des branches au fur et à mesure qu'elles s'épaississent lorsqu'elles sont proches du fond. Avec un angle de 0°, les branches auront une épaisseur uniforme sur toute leur longueur. Donner un peu d'angle permet d'augmenter la stabilité du support arborescent."
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution label"
msgid "Tree Support Collision Resolution"
-msgstr ""
+msgstr "Résolution de collision du support arborescent"
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution description"
msgid "Resolution to compute collisions with to avoid hitting the model. Setting this lower will produce more accurate trees that fail less often, but increases slicing time dramatically."
-msgstr ""
+msgstr "Résolution servant à calculer les collisions afin d'éviter de heurter le modèle. Plus ce paramètre est faible, plus les arborescences seront précises et stables, mais cela augmente considérablement le temps de découpage."
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness label"
msgid "Tree Support Wall Thickness"
-msgstr ""
+msgstr "Épaisseur de la paroi du support arborescent"
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness description"
msgid "The thickness of the walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "Épaisseur des parois des branches du support arborescent. Les parois plus épaisses prennent plus de temps à imprimer, mais ne tombent pas aussi facilement."
#: fdmprinter.def.json
msgctxt "support_tree_wall_count label"
msgid "Tree Support Wall Line Count"
-msgstr ""
+msgstr "Nombre de lignes de la paroi du support arborescent"
#: fdmprinter.def.json
msgctxt "support_tree_wall_count description"
msgid "The number of walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "Nombre de parois des branches du support arborescent. Les parois plus épaisses prennent plus de temps à imprimer, mais ne tombent pas aussi facilement."
#: fdmprinter.def.json
msgctxt "slicing_tolerance label"
@@ -4416,12 +4417,12 @@ msgstr "Une liste de sens de ligne (exprimés en nombres entiers) à utiliser lo
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization label"
msgid "Infill Travel Optimization"
-msgstr ""
+msgstr "Optimisation du déplacement de remplissage"
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization description"
msgid "When enabled, the order in which the infill lines are printed is optimized to reduce the distance travelled. The reduction in travel time achieved very much depends on the model being sliced, infill pattern, density, etc. Note that, for some models that have many small areas of infill, the time to slice the model may be greatly increased."
-msgstr ""
+msgstr "Lorsque cette option est activée, l'ordre dans lequel les lignes de remplissage sont imprimées est optimisé pour réduire la distance parcourue. La réduction du temps de parcours dépend en grande partie du modèle à découper, du type de remplissage, de la densité, etc. Remarque : pour certains modèles possédant beaucoup de petites zones de remplissage, le temps de découpe du modèle peut en être considérablement augmenté."
#: fdmprinter.def.json
msgctxt "material_flow_dependent_temperature label"
@@ -4671,7 +4672,7 @@ msgstr "Insert en spaghettis"
#: fdmprinter.def.json
msgctxt "spaghetti_inset description"
msgid "The offset from the walls from where the spaghetti infill will be printed."
-msgstr "Le décalage à partir des parois depuis lesquelles le remplissage en spaghettis sera imprimé."
+msgstr "L'offset à partir des parois depuis lesquelles le remplissage en spaghettis sera imprimé."
#: fdmprinter.def.json
msgctxt "spaghetti_flow label"
@@ -4776,7 +4777,7 @@ msgstr "Distance moyenne entre les points ajoutés aléatoirement sur chaque seg
#: fdmprinter.def.json
msgctxt "flow_rate_max_extrusion_offset label"
msgid "Flow rate compensation max extrusion offset"
-msgstr "Décalage d'extrusion max. pour compensation du débit"
+msgstr "Offset d'extrusion max. pour compensation du débit"
#: fdmprinter.def.json
msgctxt "flow_rate_max_extrusion_offset description"
@@ -5055,42 +5056,42 @@ msgstr "Distance entre la buse et les lignes descendantes horizontalement. Un es
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled label"
msgid "Use adaptive layers"
-msgstr ""
+msgstr "Utiliser des couches adaptatives"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled description"
msgid "Adaptive layers computes the layer heights depending on the shape of the model."
-msgstr ""
+msgstr "Cette option calcule la hauteur des couches en fonction de la forme du modèle."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation label"
msgid "Adaptive layers maximum variation"
-msgstr ""
+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 in mm."
-msgstr ""
+msgstr "Hauteur maximale autorisée par rapport à la couche de base, en mm."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step label"
msgid "Adaptive layers variation step size"
-msgstr ""
+msgstr "Taille des étapes de variation des couches adaptatives"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step description"
msgid "The difference in height of the next layer height compared to the previous one."
-msgstr ""
+msgstr "Différence de hauteur de la couche suivante par rapport à la précédente."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold label"
msgid "Adaptive layers threshold"
-msgstr ""
+msgstr "Limite des couches adaptatives"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold description"
msgid "Threshold whether to use a smaller layer or not. This number is compared to the tan of the steepest slope in a layer."
-msgstr ""
+msgstr "Limite indiquant d'utiliser ou non une couche plus petite. Ce nombre est comparé à la tangente de la pente la plus raide d'une couche."
#: fdmprinter.def.json
msgctxt "command_line_settings label"
@@ -5140,7 +5141,7 @@ msgstr "Position z de la maille"
#: fdmprinter.def.json
msgctxt "mesh_position_z description"
msgid "Offset applied to the object in the z direction. With this you can perform what was used to be called 'Object Sink'."
-msgstr "Décalage appliqué à l'objet dans le sens z. Cela vous permet d'exécuter ce que l'on appelait « Affaissement de l'objet »."
+msgstr "Offset appliqué à l'objet dans le sens z. Cela vous permet d'exécuter ce que l'on appelait « Affaissement de l'objet »."
#: fdmprinter.def.json
msgctxt "mesh_rotation_matrix label"
diff --git a/resources/i18n/it_IT/cura.po b/resources/i18n/it_IT/cura.po
index f4820d1aa7..d6f066aa22 100644
--- a/resources/i18n/it_IT/cura.po
+++ b/resources/i18n/it_IT/cura.po
@@ -1,15 +1,15 @@
# Cura
-# Copyright (C) 2017 Ultimaker
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
-"PO-Revision-Date: 2017-11-30 13:05+0100\n"
-"Last-Translator: Bothof \n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
+"PO-Revision-Date: 2018-02-13 13:15+0100\n"
+"Last-Translator: Crea-3D \n"
"Language-Team: Italian\n"
"Language: it_IT\n"
"MIME-Version: 1.0\n"
@@ -105,12 +105,12 @@ msgstr "Visualizza registro modifiche"
#: /home/ruben/Projects/Cura/plugins/ProfileFlattener/ProfileFlattener.py:20
msgctxt "@item:inmenu"
msgid "Flatten active settings"
-msgstr "Impostazioni attive profilo appiattito"
+msgstr "Resetta impostazioni attive"
#: /home/ruben/Projects/Cura/plugins/ProfileFlattener/ProfileFlattener.py:32
msgctxt "@info:status"
msgid "Profile has been flattened & activated."
-msgstr "Il profilo è stato appiattito e attivato."
+msgstr "Il profilo è stato resettato e attivato."
#: /home/ruben/Projects/Cura/plugins/USBPrinting/USBPrinterOutputDevice.py:27
msgctxt "@item:inmenu"
@@ -166,7 +166,7 @@ msgstr "Impossibile avviare un nuovo processo di stampa perché la stampante non
#: /home/ruben/Projects/Cura/cura/CuraApplication.py:1496
msgctxt "@info:title"
msgid "Warning"
-msgstr "Avvertenza"
+msgstr "Attenzione"
#: /home/ruben/Projects/Cura/plugins/USBPrinting/USBPrinterOutputDeviceManager.py:103
msgctxt "@info"
@@ -187,7 +187,7 @@ msgstr "Firmware stampante"
#: /home/ruben/Projects/Cura/plugins/PrepareStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Prepare"
-msgstr ""
+msgstr "Prepara"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:23
msgctxt "@action:button Preceded by 'Ready to'."
@@ -261,7 +261,7 @@ msgstr "Rimuovi"
#, python-brace-format
msgctxt "@action"
msgid "Eject removable device {0}"
-msgstr "Rimuovi il dispositivo rimovibile {0}"
+msgstr "Espelli il dispositivo rimovibile {0}"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:156
#, python-brace-format
@@ -444,7 +444,7 @@ msgstr "Sei sicuro di voler stampare con la configurazione selezionata?"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:748
msgctxt "@label"
msgid "There is a mismatch between the configuration or calibration of the printer and Cura. For the best result, always slice for the PrintCores and materials that are inserted in your printer."
-msgstr "Le configurazioni o la calibrazione della stampante e di Cura non corrispondono. Per ottenere i migliori risultati, sezionare sempre per i PrintCore e i materiali inseriti nella stampante utilizzata."
+msgstr "Le configurazioni o la calibrazione della stampante e di Cura non corrispondono. Per risultati ottimali, sezionare sempre i PrintCore e i materiali inseriti nella stampante utilizzata."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:754
msgctxt "@window:title"
@@ -506,7 +506,7 @@ msgstr "Desideri utilizzare la configurazione corrente della tua stampante in Cu
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:1295
msgctxt "@label"
msgid "The PrintCores and/or materials on your printer differ from those within your current project. For the best result, always slice for the PrintCores and materials that are inserted in your printer."
-msgstr "I PrintCore e/o i materiali sulla stampante differiscono da quelli contenuti nel tuo attuale progetto. Per ottenere i risultati migliori, sezionare sempre per i PrintCore e i materiali inseriti nella stampante utilizzata."
+msgstr "I PrintCore e/o i materiali sulla stampante differiscono da quelli contenuti nel tuo attuale progetto. Per risultati ottimali, sezionare sempre i PrintCore e i materiali inseriti nella stampante utilizzata."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:112
msgid "This printer is not set up to host a group of connected Ultimaker 3 printers."
@@ -521,7 +521,7 @@ msgstr "Questa stampante fa da host per un gruppo di {count} stampanti Ultimaker
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:114
#, python-brace-format
msgid "{printer_name} has finished printing '{job_name}'. Please collect the print and confirm clearing the build plate."
-msgstr "{printer_name} ha terminato la stampa '{job_name}'. Raccogliere la stampa e confermare la liberazione del piano di stampa."
+msgstr "{printer_name} ha terminato la stampa '{job_name}'. Rimuovere la stampa e confermare la pulizia del piano di stampa."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:115
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:533
@@ -560,7 +560,7 @@ msgstr "Apre l'interfaccia processi di stampa sul browser."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:239
msgctxt "@label Printer name"
msgid "Unknown"
-msgstr ""
+msgstr "Sconosciuto"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:505
#, python-brace-format
@@ -579,7 +579,7 @@ msgstr "Stampa finita"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:282
msgctxt "@label:status"
msgid "Action required"
-msgstr "Richiede un'azione"
+msgstr "Azione richiesta"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:656
#, python-brace-format
@@ -595,7 +595,7 @@ msgstr "Collega tramite rete"
#: /home/ruben/Projects/Cura/plugins/MonitorStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Monitor"
-msgstr ""
+msgstr "Controlla"
#: /home/ruben/Projects/Cura/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py:66
#, python-brace-format
@@ -622,7 +622,7 @@ msgstr "Non è possibile accedere alle informazioni di aggiornamento."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:579
msgctxt "@info:status"
msgid "SolidWorks reported errors, while opening your file. We recommend to solve these issues inside SolidWorks itself."
-msgstr ""
+msgstr "SolidWorks ha segnalato errori all’apertura del file. Si consiglia di risolvere queste problematiche all’interno di SolidWorks stesso."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:591
msgctxt "@info:status"
@@ -630,7 +630,7 @@ msgid ""
"Found no models inside your drawing. Could you please check it's content again and make sure one part or assembly is inside?\n"
"\n"
" Thanks!."
-msgstr ""
+msgstr "Nessun modello trovato nel disegno. Si prega di controllare nuovamente il contenuto e accertarsi che all’interno vi sia un componente o gruppo.\n\n Grazie."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:595
msgctxt "@info:status"
@@ -638,7 +638,7 @@ msgid ""
"Found more then one part or assembly inside your drawing. We currently only support drawings with exactly one part or assembly inside.\n"
"\n"
"Sorry!"
-msgstr ""
+msgstr "Trovato più di un componente o gruppo all’interno del disegno. Attualmente sono supportati solo i disegni con esattamente un componente o gruppo all’interno.\n\n Spiacenti."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:25
msgctxt "@item:inlistbox"
@@ -653,7 +653,7 @@ msgstr "File gruppo SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:33
msgctxt "@item:inlistbox"
msgid "SolidWorks drawing file"
-msgstr ""
+msgstr "File disegno SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:48
msgctxt "@info:status"
@@ -663,7 +663,7 @@ msgid ""
"\n"
"With kind regards\n"
" - Thomas Karl Pietrowski"
-msgstr ""
+msgstr "Gentile cliente,\nnon abbiamo trovato un’installazione valida di SolidWorks nel suo sistema. Questo significa che SolidWorks non è installato o che non possiede una licenza valida. La invitiamo a verificare che l’esecuzione di SolidWorks avvenga senza problemi e/o a contattare il suo ICT.\n\nCordiali saluti\n - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:57
msgctxt "@info:status"
@@ -673,7 +673,7 @@ msgid ""
"\n"
"With kind regards\n"
" - Thomas Karl Pietrowski"
-msgstr ""
+msgstr "Gentile cliente,\nattualmente ha in esecuzione questo plugin su un sistema operativo diverso da Windows. Questo plugin funziona solo su Windows con SolidWorks installato, con inclusa una licenza valida. Si prega di installare questo plugin su una macchina Windows con SolidWorks installato.\n\nCordiali saluti\n - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:70
msgid "Configure"
@@ -681,12 +681,12 @@ msgstr "Configura"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:71
msgid "Installation guide for SolidWorks macro"
-msgstr ""
+msgstr "Guida per l’installazione di macro SolidWorks"
#: /home/ruben/Projects/Cura/plugins/SimulationView/__init__.py:14
msgctxt "@item:inlistbox"
msgid "Layer view"
-msgstr "Visualizzazione strato"
+msgstr "Visualizzazione layer"
#: /home/ruben/Projects/Cura/plugins/SimulationView/SimulationView.py:103
msgctxt "@info:status"
@@ -705,7 +705,7 @@ msgstr "Modifica G-code"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:43
msgctxt "@info"
msgid "Cura collects anonymized usage statistics."
-msgstr ""
+msgstr "Cura raccoglie statistiche di utilizzo in forma anonima."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:46
msgctxt "@info:title"
@@ -715,22 +715,22 @@ msgstr "Acquisizione dati"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:48
msgctxt "@action:button"
msgid "Allow"
-msgstr ""
+msgstr "Consenti"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:49
msgctxt "@action:tooltip"
msgid "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing."
-msgstr ""
+msgstr "Consente a Cura di inviare in forma anonima statistiche d’uso, riguardanti alcune delle preferenze e impostazioni, la versione cura e una serie di modelli in sezionamento, per aiutare a dare priorità a miglioramenti futuri in Cura."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:50
msgctxt "@action:button"
msgid "Disable"
-msgstr ""
+msgstr "Disabilita"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:51
msgctxt "@action:tooltip"
msgid "Don't allow Cura to send anonymized usage statistics. You can enable it again in the preferences."
-msgstr ""
+msgstr "Non consente a Cura di inviare statistiche di utilizzo in forma anonima. È possibile riabilitare nelle preferenze."
#: /home/ruben/Projects/Cura/plugins/LegacyProfileReader/__init__.py:14
msgctxt "@item:inlistbox"
@@ -740,14 +740,14 @@ msgstr "Profili Cura 15.04"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/__init__.py:15
msgctxt "@item:inlistbox"
msgid "Blender file"
-msgstr ""
+msgstr "File Blender"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/CadIntegrationUtils/CommonReader.py:199
msgctxt "@info:status"
msgid ""
"Could not export using \"{}\" quality!\n"
"Felt back to \"{}\"."
-msgstr ""
+msgstr "Impossibile esportare utilizzando qualità \"{}\" quality!\nTornato a \"{}\"."
#: /home/ruben/Projects/Cura/plugins/GCodeProfileReader/__init__.py:14
#: /home/ruben/Projects/Cura/plugins/GCodeReader/__init__.py:14
@@ -783,7 +783,7 @@ msgstr "Immagine GIF"
#: /home/ruben/Projects/Cura/plugins/CuraEngineBackend/CuraEngineBackend.py:299
msgctxt "@info:status"
msgid "Unable to slice with the current material as it is incompatible with the selected machine or configuration."
-msgstr "Impossibile eseguire il sezionamento con il materiale corrente in quanto incompatibile con la macchina o la configurazione selezionata."
+msgstr "Impossibile eseguire lo slicing con il materiale corrente in quanto incompatibile con la macchina o la configurazione selezionata."
#: /home/ruben/Projects/Cura/plugins/CuraEngineBackend/CuraEngineBackend.py:299
#: /home/ruben/Projects/Cura/plugins/CuraEngineBackend/CuraEngineBackend.py:327
@@ -792,24 +792,24 @@ msgstr "Impossibile eseguire il sezionamento con il materiale corrente in quanto
#: /home/ruben/Projects/Cura/plugins/CuraEngineBackend/CuraEngineBackend.py:366
msgctxt "@info:title"
msgid "Unable to slice"
-msgstr "Sezionamento impossibile"
+msgstr "Slicing impossibile"
#: /home/ruben/Projects/Cura/plugins/CuraEngineBackend/CuraEngineBackend.py:326
#, python-brace-format
msgctxt "@info:status"
msgid "Unable to slice with the current settings. The following settings have errors: {0}"
-msgstr "Impossibile eseguire il sezionamento con le impostazioni attuali. Le seguenti impostazioni presentano errori: {0}"
+msgstr "Impossibile eseguire lo slicing con le impostazioni attuali. Le seguenti impostazioni presentano errori: {0}"
#: /home/ruben/Projects/Cura/plugins/CuraEngineBackend/CuraEngineBackend.py:348
#, python-brace-format
msgctxt "@info:status"
msgid "Unable to slice due to some per-model settings. The following settings have errors on one or more models: {error_labels}"
-msgstr "Impossibile eseguire il sezionamento a causa di alcune impostazioni per modello. Le seguenti impostazioni presentano errori su uno o più modelli: {error_labels}"
+msgstr "Impossibile eseguire lo slicing a causa di alcune impostazioni del modello. Le seguenti impostazioni presentano errori su uno o più modelli: {error_labels}"
#: /home/ruben/Projects/Cura/plugins/CuraEngineBackend/CuraEngineBackend.py:356
msgctxt "@info:status"
msgid "Unable to slice because the prime tower or prime position(s) are invalid."
-msgstr "Impossibile eseguire il sezionamento perché la torre di innesco o la posizione di innesco non sono valide."
+msgstr "Impossibile eseguire lo slicing perché la prime tower o la prime position non sono valide."
#: /home/ruben/Projects/Cura/plugins/CuraEngineBackend/CuraEngineBackend.py:365
msgctxt "@info:status"
@@ -843,21 +843,21 @@ msgstr "Installazione"
#: /home/ruben/Projects/Cura/plugins/cura-siemensnx-plugin/Installer.py:43
msgid "Failed to copy Siemens NX plugins files. Please check your UGII_USER_DIR. It is not set to a directory."
-msgstr "Impossibile copiare i file di plugin Siemens NX. Controllare UGII_USER_DIR. Non è assegnato ad alcuna directory."
+msgstr "Impossibile copiare i file dei plugin Siemens NX. Controllare UGII_USER_DIR. Non è assegnato ad alcuna directory."
#: /home/ruben/Projects/Cura/plugins/cura-siemensnx-plugin/Installer.py:50
#: /home/ruben/Projects/Cura/plugins/cura-siemensnx-plugin/Installer.py:59
#: /home/ruben/Projects/Cura/plugins/cura-siemensnx-plugin/Installer.py:81
msgid "Successfully installed Siemens NX Cura plugin."
-msgstr "Installato correttamente plugin Siemens NX Cura."
+msgstr "Siemens NX Cura plugin installato correttamente."
#: /home/ruben/Projects/Cura/plugins/cura-siemensnx-plugin/Installer.py:65
msgid "Failed to copy Siemens NX plugins files. Please check your UGII_USER_DIR."
-msgstr "Impossibile copiare i file di plugin Siemens NX. Controllare UGII_USER_DIR."
+msgstr "Impossibile copiare i file dei plugin Siemens NX. Controllare UGII_USER_DIR."
#: /home/ruben/Projects/Cura/plugins/cura-siemensnx-plugin/Installer.py:85
msgid "Failed to install Siemens NX plugin. Could not set environment variable UGII_USER_DIR for Siemens NX."
-msgstr "Impossibile installare plugin Siemens NX. Impossibile impostare la variabile di ambiente UGII_USER_DIR per Siemens NX."
+msgstr "Impossibile installare il plugin Siemens NX. Impossibile impostare la variabile di ambiente UGII_USER_DIR per Siemens NX."
#: /home/ruben/Projects/Cura/plugins/3MFReader/WorkspaceDialog.py:165
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:590
@@ -887,12 +887,12 @@ msgstr "Ugello"
#, python-brace-format
msgctxt "@info:status"
msgid "Failed to get plugin ID from {0}"
-msgstr "Impossibile ottenere ID plugin da {0}"
+msgstr "Impossibile ottenere ID del plugin da {0}"
#: /home/ruben/Projects/Cura/plugins/PluginBrowser/PluginBrowser.py:153
msgctxt "@info:tile"
msgid "Warning"
-msgstr "Avvertenza"
+msgstr "Attenzione"
#: /home/ruben/Projects/Cura/plugins/PluginBrowser/PluginBrowser.py:191
msgctxt "@window:title"
@@ -912,18 +912,18 @@ msgstr "File G"
#: /home/ruben/Projects/Cura/plugins/GCodeReader/FlavorParser.py:321
msgctxt "@info:status"
msgid "Parsing G-code"
-msgstr "Parsing codice G"
+msgstr "Analisi G-code"
#: /home/ruben/Projects/Cura/plugins/GCodeReader/FlavorParser.py:323
#: /home/ruben/Projects/Cura/plugins/GCodeReader/FlavorParser.py:464
msgctxt "@info:title"
msgid "G-code Details"
-msgstr "Dettagli codice G"
+msgstr "Dettagli G-code"
#: /home/ruben/Projects/Cura/plugins/GCodeReader/FlavorParser.py:462
msgctxt "@info:generic"
msgid "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."
-msgstr "Verifica che il codice G sia idoneo alla tua stampante e alla sua configurazione prima di trasmettere il file. La rappresentazione del codice G potrebbe non essere accurata."
+msgstr "Verifica che il G-code sia idoneo alla tua stampante e alla sua configurazione prima di trasmettere il file. La rappresentazione del G-code potrebbe non essere accurata."
#: /home/ruben/Projects/Cura/plugins/CuraProfileWriter/__init__.py:14
#: /home/ruben/Projects/Cura/plugins/CuraProfileReader/__init__.py:14
@@ -934,12 +934,12 @@ msgstr "Profilo Cura"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:12
msgctxt "@item:inmenu"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Assistente profilo"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:17
msgctxt "@item:inlistbox"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Assistente profilo"
#: /home/ruben/Projects/Cura/plugins/3MFWriter/__init__.py:30
msgctxt "@item:inlistbox"
@@ -1137,13 +1137,13 @@ msgstr "Impossibile importare il profilo da {0}: {
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "This profile {0} contains incorrect data, could not import it."
-msgstr ""
+msgstr "Questo profilo {0} contiene dati errati, impossibile importarlo."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:240
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "The machine defined in profile {0} doesn't match with your current machine, could not import it."
-msgstr ""
+msgstr "La macchina definita nel profilo {0} non corrisponde alla macchina corrente, impossibile importarlo."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:274
#, python-brace-format
@@ -1155,7 +1155,7 @@ msgstr "Profilo importato correttamente {0}"
#, python-brace-format
msgctxt "@info:status"
msgid "File {0} does not contain any valid profile."
-msgstr ""
+msgstr "Il file {0} non contiene nessun profilo valido."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:280
#, python-brace-format
@@ -1183,7 +1183,7 @@ msgstr "Impossibile trovare un tipo qualità {0} per la configurazione corrente.
#, python-brace-format
msgctxt "@label"
msgid "Group #{group_nr}"
-msgstr ""
+msgstr "Gruppo #{group_nr}"
#: /home/ruben/Projects/Cura/cura/BuildVolume.py:100
msgctxt "@info:status"
@@ -1234,7 +1234,7 @@ msgstr "Impossibile individuare posizione"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:81
msgctxt "@title:window"
msgid "Crash Report"
-msgstr "Rapporto su crash"
+msgstr "Rapporto sul crash"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:94
msgctxt "@label crash message"
@@ -1242,7 +1242,7 @@ msgid ""
"
A fatal error has occurred. Please send us this Crash Report to fix the problem
\n"
"
Please use the \"Send report\" button to post a bug report automatically to our servers
\n"
" "
-msgstr ""
+msgstr "
Si è verificato un errore fatale. Si prega di inviare questo Report su crash per correggere il problema
\n
Usare il pulsante “Invia report\" per inviare automaticamente una segnalazione errore ai nostri server
"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:147
msgctxt "@title:groupbox"
msgid "Error traceback"
-msgstr ""
+msgstr "Analisi errori"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:214
msgctxt "@title:groupbox"
@@ -1347,13 +1347,13 @@ msgstr "%(width).1f x %(depth).1f x %(height).1f mm"
#, python-brace-format
msgctxt "@info:status"
msgid "Only one G-code file can be loaded at a time. Skipped importing {0}"
-msgstr "È possibile caricare un solo file codice G per volta. Importazione saltata {0}"
+msgstr "È possibile caricare un solo file G-code per volta. Importazione saltata {0}"
#: /home/ruben/Projects/Cura/cura/CuraApplication.py:1426
#, python-brace-format
msgctxt "@info:status"
msgid "Can't open any other file if G-code is loading. Skipped importing {0}"
-msgstr "Impossibile aprire altri file durante il caricamento del codice G. Importazione saltata {0}"
+msgstr "Impossibile aprire altri file durante il caricamento del G-code. Importazione saltata {0}"
#: /home/ruben/Projects/Cura/cura/CuraApplication.py:1495
msgctxt "@info:status"
@@ -1426,7 +1426,7 @@ msgstr "Versione GCode"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:181
msgctxt "@label"
msgid "Printhead Settings"
-msgstr "Impostazioni della testina di stampa"
+msgstr "Impostazioni della testa di stampa"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:191
msgctxt "@label"
@@ -1436,7 +1436,7 @@ msgstr "X min"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:192
msgctxt "@tooltip"
msgid "Distance from the left of the printhead to the center of the nozzle. Used to prevent colissions between previous prints and the printhead when printing \"One at a Time\"."
-msgstr "Distanza tra il lato sinistro della testina di stampa e il centro dell'ugello. Utilizzata per evitare collisioni tra le stampe precedenti e la testina di stampa durante la stampa \"Uno alla volta\"."
+msgstr "Distanza tra il lato sinistro della testa di stampa e il centro dell'ugello. Utilizzata per evitare collisioni tra le stampe precedenti e la testa di stampa durante la stampa \"Uno alla volta\"."
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:201
msgctxt "@label"
@@ -1446,7 +1446,7 @@ msgstr "Y min"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:202
msgctxt "@tooltip"
msgid "Distance from the front of the printhead to the center of the nozzle. Used to prevent colissions between previous prints and the printhead when printing \"One at a Time\"."
-msgstr "Distanza tra il lato anteriore della testina di stampa e il centro dell'ugello. Utilizzata per evitare collisioni tra le stampe precedenti e la testina di stampa durante la stampa \"Uno alla volta\"."
+msgstr "Distanza tra il lato anteriore della testa di stampa e il centro dell'ugello. Utilizzata per evitare collisioni tra le stampe precedenti e la testa di stampa durante la stampa \"Uno alla volta\"."
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:211
msgctxt "@label"
@@ -1456,7 +1456,7 @@ msgstr "X max"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:212
msgctxt "@tooltip"
msgid "Distance from the right of the printhead to the center of the nozzle. Used to prevent colissions between previous prints and the printhead when printing \"One at a Time\"."
-msgstr "Distanza tra il lato destro della testina di stampa e il centro dell'ugello. Utilizzata per evitare collisioni tra le stampe precedenti e la testina di stampa durante la stampa \"Uno alla volta\"."
+msgstr "Distanza tra il lato destro della testa di stampa e il centro dell'ugello. Utilizzata per evitare collisioni tra le stampe precedenti e la testa di stampa durante la stampa \"Uno alla volta\"."
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:221
msgctxt "@label"
@@ -1466,7 +1466,7 @@ msgstr "Y max"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:222
msgctxt "@tooltip"
msgid "Distance from the rear of the printhead to the center of the nozzle. Used to prevent colissions between previous prints and the printhead when printing \"One at a Time\"."
-msgstr "Distanza tra il lato posteriore della testina di stampa e il centro dell'ugello. Utilizzata per evitare collisioni tra le stampe precedenti e la testina di stampa durante la stampa \"Uno alla volta\"."
+msgstr "Distanza tra il lato posteriore della testa di stampa e il centro dell'ugello. Utilizzata per evitare collisioni tra le stampe precedenti e la testa di stampa durante la stampa \"Uno alla volta\"."
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:234
msgctxt "@label"
@@ -1516,12 +1516,12 @@ msgstr "Dimensione ugello"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:393
msgctxt "@label"
msgid "Compatible material diameter"
-msgstr ""
+msgstr "Diametro del materiale compatibile"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:395
msgctxt "@tooltip"
msgid "The nominal diameter of filament supported by the printer. The exact diameter will be overridden by the material and/or the profile."
-msgstr "Diametro nominale del filamento supportato dalla stampante. Il diametro esatto verrà sovrapposto dal materiale e/o dal profilo."
+msgstr "Diametro nominale del filamento supportato dalla stampante. Il diametro esatto verrà sovrascritto dal materiale e/o dal profilo."
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:411
msgctxt "@label"
@@ -1536,12 +1536,12 @@ msgstr "Scostamento Y ugello"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:444
msgctxt "@label"
msgid "Extruder Start Gcode"
-msgstr "Codice G avvio estrusore"
+msgstr "Gcode avvio estrusore"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:462
msgctxt "@label"
msgid "Extruder End Gcode"
-msgstr "Codice G fine estrusore"
+msgstr "Gcode fine estrusore"
#: /home/ruben/Projects/Cura/plugins/ChangeLogPlugin/ChangeLog.qml:18
msgctxt "@label"
@@ -1618,10 +1618,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 dall’elenco 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 Gcode alla stampante.\n\nSelezionare la stampante dall’elenco seguente:"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:75
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:44
@@ -1661,12 +1658,12 @@ msgstr "Tipo"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:233
msgctxt "@label Printer name"
msgid "Ultimaker 3"
-msgstr ""
+msgstr "Ultimaker 3"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:236
msgctxt "@label Printer name"
msgid "Ultimaker 3 Extended"
-msgstr ""
+msgstr "Ultimaker 3 Extended"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:252
msgctxt "@label"
@@ -1718,7 +1715,7 @@ msgstr "OK"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrintWindow.qml:24
msgctxt "@title:window"
msgid "Print over network"
-msgstr "Stampa sulla rete"
+msgstr "Stampa tramite rete"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrintWindow.qml:92
msgctxt "@action:button"
@@ -1733,7 +1730,7 @@ msgstr "%1 non è configurata per supportare la connessione di un gruppo di stam
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml:55
msgctxt "@label link to connect manager"
msgid "Add/Remove printers"
-msgstr ""
+msgstr "Aggiungi/Rimuovi stampanti"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/OpenPanelButton.qml:14
msgctxt "@info:tooltip"
@@ -1773,7 +1770,7 @@ msgstr "Persa connessione con la stampante"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:47
msgctxt "@label Printer status"
msgid "Unknown"
-msgstr ""
+msgstr "Sconosciuto"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:257
msgctxt "@label:status"
@@ -1819,7 +1816,7 @@ msgstr "Finisce alle: "
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:405
msgctxt "@label"
msgid "Clear build plate"
-msgstr "Cancellare piano di stampa"
+msgstr "Pulire piano di stampa"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:414
msgctxt "@label"
@@ -1869,164 +1866,164 @@ msgstr "Attiva la configurazione"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:21
msgctxt "@title:window"
msgid "SolidWorks: Export wizard"
-msgstr ""
+msgstr "SolidWorks: procedura guidata per l’esportazione"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:45
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:140
msgctxt "@action:label"
msgid "Quality:"
-msgstr ""
+msgstr "Qualità:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:78
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:179
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (3D-printing)"
-msgstr ""
+msgstr "Fine (stampa 3D)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:79
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:180
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (3D-printing)"
-msgstr ""
+msgstr "Grossolana (stampa 3D)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:80
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:181
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (SolidWorks)"
-msgstr ""
+msgstr "Fine (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:81
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:182
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (SolidWorks)"
-msgstr ""
+msgstr "Grossolana (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:94
msgctxt "@text:window"
msgid "Show this dialog again"
-msgstr ""
+msgstr "Mostra nuovamente questa finestra di dialogo"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:104
msgctxt "@action:button"
msgid "Continue"
-msgstr ""
+msgstr "Continua"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:116
msgctxt "@action:button"
msgid "Abort"
-msgstr ""
+msgstr "Interrompi"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:21
msgctxt "@title:window"
msgid "How to install Cura SolidWorks macro"
-msgstr ""
+msgstr "Come installare la macro Cura SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:62
msgctxt "@description:label"
msgid "Steps:"
-msgstr ""
+msgstr "Fasi:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:140
msgctxt "@action:button"
msgid ""
"Open the directory\n"
"with macro and icon"
-msgstr ""
+msgstr "Aprire la directory\ncon macro e icona"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:160
msgctxt "@description:label"
msgid "Instructions:"
-msgstr ""
+msgstr "Istruzioni:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:202
msgctxt "@action:playpause"
msgid "Play"
-msgstr ""
+msgstr "Riproduci"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:206
msgctxt "@action:playpause"
msgid "Pause"
-msgstr ""
+msgstr "Pausa"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:268
msgctxt "@action:button"
msgid "Previous Step"
-msgstr ""
+msgstr "Fase precedente"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:283
msgctxt "@action:button"
msgid "Done"
-msgstr ""
+msgstr "Eseguito"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:287
msgctxt "@action:button"
msgid "Next Step"
-msgstr ""
+msgstr "Fase successiva"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:21
msgctxt "@title:window"
msgid "SolidWorks plugin: Configuration"
-msgstr ""
+msgstr "Plugin SolidWorks: configurazione"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:39
msgctxt "@title:tab"
msgid "Conversion settings"
-msgstr ""
+msgstr "Impostazioni di conversione"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:66
msgctxt "@label"
msgid "First choice:"
-msgstr ""
+msgstr "Prima scelta:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:86
msgctxt "@text:menu"
msgid "Latest installed version (Recommended)"
-msgstr ""
+msgstr "Ultima versione installata (consigliata)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:95
msgctxt "@text:menu"
msgid "Default version"
-msgstr ""
+msgstr "Versione predefinita"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:193
msgctxt "@label"
msgid "Show wizard before opening SolidWorks files"
-msgstr ""
+msgstr "Mostra la procedura guidata prima di aprire i file SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:203
msgctxt "@label"
msgid "Automatically rotate opened file into normed orientation"
-msgstr ""
+msgstr "Ruota automaticamente il file aperto nell’orientamento corretto"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:210
msgctxt "@title:tab"
msgid "Installation(s)"
-msgstr ""
+msgstr "Installazione(i)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:284
msgctxt "@label"
msgid "COM service found"
-msgstr ""
+msgstr "Servizio COM trovato"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:295
msgctxt "@label"
msgid "Executable found"
-msgstr ""
+msgstr "Eseguibile trovato"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:306
msgctxt "@label"
msgid "COM starting"
-msgstr ""
+msgstr "COM in avvio"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:317
msgctxt "@label"
msgid "Revision number"
-msgstr ""
+msgstr "Numero di revisione"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:328
msgctxt "@label"
msgid "Functions available"
-msgstr ""
+msgstr "Funzioni disponibili"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:341
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:264
@@ -2057,7 +2054,7 @@ msgstr "Velocità"
#: /home/ruben/Projects/Cura/plugins/SimulationView/SimulationView.qml:144
msgctxt "@label:listbox"
msgid "Layer thickness"
-msgstr "Spessore strato"
+msgstr "Spessore layer"
#: /home/ruben/Projects/Cura/plugins/SimulationView/SimulationView.qml:185
msgctxt "@label"
@@ -2092,7 +2089,7 @@ msgstr "Mostra solo strati superiori"
#: /home/ruben/Projects/Cura/plugins/SimulationView/SimulationView.qml:339
msgctxt "@label"
msgid "Show 5 Detailed Layers On Top"
-msgstr "Mostra 5 strati superiori in dettaglio"
+msgstr "Mostra 5 layer superiori in dettaglio"
#: /home/ruben/Projects/Cura/plugins/SimulationView/SimulationView.qml:350
msgctxt "@label"
@@ -2212,32 +2209,32 @@ msgstr "Smoothing"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:38
msgctxt "@label"
msgid "Mesh Type"
-msgstr ""
+msgstr "Tipo di maglia"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:69
msgctxt "@label"
msgid "Normal model"
-msgstr ""
+msgstr "Modello normale"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:76
msgctxt "@label"
msgid "Print as support"
-msgstr ""
+msgstr "Stampa come supporto"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:84
msgctxt "@label"
msgid "Don't support overlap with other models"
-msgstr ""
+msgstr "Non supporta sovrapposizione con altri modelli"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:92
msgctxt "@label"
msgid "Modify settings for overlap with other models"
-msgstr ""
+msgstr "Modifica impostazioni per sovrapposizione con altri modelli"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:100
msgctxt "@label"
msgid "Modify settings for infill of other models"
-msgstr ""
+msgstr "Modifica impostazioni per riempimento di altri modelli"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:333
msgctxt "@action:button"
@@ -2423,10 +2420,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/PluginBrowser/PluginBrowser.qml:242
msgctxt "@action:button"
@@ -2457,7 +2451,7 @@ msgstr "Seleziona qualsiasi aggiornamento realizzato per questa Ultimaker 2."
#: /home/ruben/Projects/Cura/plugins/UltimakerMachineActions/UM2UpgradeSelectionMachineAction.qml:45
msgctxt "@label"
msgid "Olsson Block"
-msgstr "Blocco Olsson"
+msgstr "Olsson Block"
#: /home/ruben/Projects/Cura/plugins/UltimakerMachineActions/BedLevelMachineAction.qml:27
msgctxt "@title"
@@ -2472,7 +2466,7 @@ msgstr "Per assicurarsi stampe di alta qualità, è ora possibile regolare il pi
#: /home/ruben/Projects/Cura/plugins/UltimakerMachineActions/BedLevelMachineAction.qml:47
msgctxt "@label"
msgid "For every position; insert a piece of paper under the nozzle and adjust the print build plate height. The print build plate height is right when the paper is slightly gripped by the tip of the nozzle."
-msgstr "Per ciascuna posizione: inserire un pezzo di carta sotto l'ugello e regolare la stampa dell'altezza del piano di stampa. L'altezza del piano di stampa è corretta quando la carta sfiora la punta dell'ugello."
+msgstr "Per ciascuna posizione: inserire un pezzo di carta sotto l'ugello e regolare l'altezza del piano di stampa. L'altezza del piano di stampa è corretta quando la carta sfiora la punta dell'ugello."
#: /home/ruben/Projects/Cura/plugins/UltimakerMachineActions/BedLevelMachineAction.qml:62
msgctxt "@action:button"
@@ -2691,9 +2685,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"
@@ -2922,12 +2914,12 @@ msgstr "Visualizza sbalzo"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:344
msgctxt "@info:tooltip"
msgid "Moves the camera so the model is in the center of the view when a model is selected"
-msgstr "Sposta la fotocamera in modo che il modello si trovi al centro della visualizzazione quando è selezionato"
+msgstr "Sposta la camera in modo che il modello si trovi al centro della visualizzazione quando è selezionato"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:349
msgctxt "@action:button"
msgid "Center camera when item is selected"
-msgstr "Centratura fotocamera alla selezione dell'elemento"
+msgstr "Centratura camera alla selezione dell'elemento"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:358
msgctxt "@info:tooltip"
@@ -2937,7 +2929,7 @@ msgstr "Il comportamento dello zoom predefinito di Cura dovrebbe essere invertit
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:363
msgctxt "@action:button"
msgid "Invert the direction of camera zoom."
-msgstr "Inverti la direzione dello zoom della fotocamera."
+msgstr "Inverti la direzione dello zoom della camera."
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:372
msgctxt "@info:tooltip"
@@ -2972,22 +2964,22 @@ msgstr "Rilascia automaticamente i modelli sul piano di stampa"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:416
msgctxt "@info:tooltip"
msgid "Show caution message in gcode reader."
-msgstr "Visualizza il messaggio di avvertimento sul lettore codice G."
+msgstr "Visualizza il messaggio di avvertimento sul lettore gcode."
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:425
msgctxt "@option:check"
msgid "Caution message in gcode reader"
-msgstr "Messaggio di avvertimento sul lettore codice G"
+msgstr "Messaggio di avvertimento sul lettore gcode"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:432
msgctxt "@info:tooltip"
msgid "Should layer be forced into compatibility mode?"
-msgstr "Lo strato deve essere forzato in modalità di compatibilità?"
+msgstr "Il layer deve essere forzato in modalità di compatibilità?"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:437
msgctxt "@option:check"
msgid "Force layer view compatibility mode (restart required)"
-msgstr "Forzare la modalità di compatibilità visualizzazione strato (riavvio necessario)"
+msgstr "Forzare la modalità di compatibilità visualizzazione layer (riavvio necessario)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:453
msgctxt "@label"
@@ -3097,27 +3089,27 @@ msgstr "Invia informazioni di stampa (anonime)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:674
msgctxt "@label"
msgid "Experimental"
-msgstr ""
+msgstr "Sperimentale"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:680
msgctxt "@info:tooltip"
msgid "Use multi build plate functionality"
-msgstr ""
+msgstr "Utilizzare la funzionalità piano di stampa multiplo"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:685
msgctxt "@option:check"
msgid "Use multi build plate functionality (restart required)"
-msgstr ""
+msgstr "Utilizzare la funzionalità piano di stampa multiplo (necessario riavvio)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:694
msgctxt "@info:tooltip"
msgid "Should newly loaded models be arranged on the build plate? Used in conjunction with multi build plate (EXPERIMENTAL)"
-msgstr ""
+msgstr "I modelli appena caricati devono essere sistemati sul piano di stampa? Utilizzato in abbinamento al piano di stampa multiplo (SPERIMENTALE)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:699
msgctxt "@option:check"
msgid "Do not arrange objects on load"
-msgstr ""
+msgstr "Non posizionare oggetti dopo il caricamento"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:15
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:514
@@ -3162,7 +3154,7 @@ msgstr "Stato:"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:190
msgctxt "@label:MonitorStatus"
msgid "Waiting for someone to clear the build plate"
-msgstr "In attesa di qualcuno che cancelli il piano di stampa"
+msgstr "In attesa che qualcuno liberi il piano di stampa"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:199
msgctxt "@label:MonitorStatus"
@@ -3361,9 +3353,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"
@@ -3471,10 +3461,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:150
msgctxt "@label:textbox"
@@ -3512,10 +3499,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."
@@ -3530,7 +3514,7 @@ msgstr "Influenzato da"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:156
msgctxt "@label"
msgid "This setting is always shared between all extruders. Changing it here will change the value for all extruders."
-msgstr ""
+msgstr "Questa impostazione è sempre condivisa tra tutti gli estrusori. La sua modifica varierà il valore per tutti gli estrusori."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:159
msgctxt "@label"
@@ -3543,10 +3527,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:288
msgctxt "@label"
@@ -3554,10 +3535,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/Sidebar.qml:128
msgctxt "@label:listbox"
@@ -3569,9 +3547,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 G-code non possono essere modificati"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:342
msgctxt "@label Hours and minutes"
@@ -3581,7 +3557,7 @@ msgstr "00h 00min"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:359
msgctxt "@tooltip"
msgid "Time specification"
-msgstr ""
+msgstr "Indicazioni di tempo"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:441
msgctxt "@label"
@@ -3623,7 +3599,7 @@ msgstr "Impostazione di stampa consigliata
Stampa con le imposta
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:596
msgctxt "@tooltip"
msgid "Custom Print Setup
Print with finegrained control over every last bit of the slicing process."
-msgstr "Impostazione di stampa personalizzata
Stampa con il controllo grana fine su ogni sezione finale del processo di sezionamento."
+msgstr "Impostazione di stampa personalizzata
Stampa con il controllo grana fine su ogni sezione finale del processo di slicing."
#: /home/ruben/Projects/Cura/resources/qml/Menus/MaterialMenu.qml:50
msgctxt "@title:menuitem %1 is the automatically selected material"
@@ -3638,12 +3614,12 @@ msgstr "&Visualizza"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:37
msgctxt "@action:inmenu menubar:view"
msgid "&Camera position"
-msgstr ""
+msgstr "&Posizione fotocamera"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:52
msgctxt "@action:inmenu menubar:view"
msgid "&Build plate"
-msgstr ""
+msgstr "&Piano di stampa"
#: /home/ruben/Projects/Cura/resources/qml/Menus/NozzleMenu.qml:40
msgctxt "@title:menuitem %1 is the nozzle currently loaded in the printer"
@@ -3728,7 +3704,7 @@ msgstr "La temperatura corrente del piano riscaldato."
#: /home/ruben/Projects/Cura/resources/qml/PrintMonitor.qml:423
msgctxt "@tooltip of temperature input"
msgid "The temperature to pre-heat the bed to."
-msgstr "La temperatura di preriscaldo del piano."
+msgstr "La temperatura di preriscaldamento del piano."
#: /home/ruben/Projects/Cura/resources/qml/PrintMonitor.qml:623
msgctxt "@button Cancel pre-heating"
@@ -3738,7 +3714,7 @@ msgstr "Annulla"
#: /home/ruben/Projects/Cura/resources/qml/PrintMonitor.qml:623
msgctxt "@button"
msgid "Pre-heat"
-msgstr "Pre-riscaldo"
+msgstr "Pre-riscaldamento"
#: /home/ruben/Projects/Cura/resources/qml/PrintMonitor.qml:650
msgctxt "@tooltip of pre-heat"
@@ -3813,27 +3789,27 @@ msgstr "E&sci"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:114
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
-msgstr ""
+msgstr "&Visualizzazione 3D"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:121
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
-msgstr ""
+msgstr "&Visualizzazione frontale"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:128
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
-msgstr ""
+msgstr "&Visualizzazione superiore"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:135
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
-msgstr ""
+msgstr "&Visualizzazione lato sinistro"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:142
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
-msgstr ""
+msgstr "&Visualizzazione lato destro"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:149
msgctxt "@action:inmenu"
@@ -3949,7 +3925,7 @@ msgstr "Sel&eziona tutti i modelli"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:332
msgctxt "@action:inmenu menubar:edit"
msgid "&Clear Build Plate"
-msgstr "&Cancellare piano di stampa"
+msgstr "&Pulire piano di stampa"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:342
msgctxt "@action:inmenu menubar:file"
@@ -3959,7 +3935,7 @@ msgstr "R&icarica tutti i modelli"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:edit"
msgid "Arrange All Models To All Build Plates"
-msgstr ""
+msgstr "Sistema tutti i modelli su tutti i piani di stampa"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:358
msgctxt "@action:inmenu menubar:edit"
@@ -3994,7 +3970,7 @@ msgstr "&Nuovo Progetto..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:402
msgctxt "@action:inmenu menubar:help"
msgid "Show Engine &Log..."
-msgstr "M&ostra log motore..."
+msgstr "M&ostra motore log..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:410
msgctxt "@action:inmenu menubar:help"
@@ -4019,7 +3995,7 @@ msgstr "Plugin installati..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:438
msgctxt "@action:inmenu menubar:view"
msgid "Expand/Collapse Sidebar"
-msgstr ""
+msgstr "Espandi/Riduci barra laterale"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:26
msgctxt "@label:PrintjobStatus"
@@ -4034,7 +4010,7 @@ msgstr "Pronto per il sezionamento"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:38
msgctxt "@label:PrintjobStatus"
msgid "Slicing..."
-msgstr "Sezionamento in corso..."
+msgstr "Slicing in corso..."
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:40
msgctxt "@label:PrintjobStatus %1 is target operation"
@@ -4044,7 +4020,7 @@ msgstr "Pronto a %1"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:42
msgctxt "@label:PrintjobStatus"
msgid "Unable to Slice"
-msgstr "Sezionamento impossibile"
+msgstr "Slicing impossibile"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:44
msgctxt "@label:PrintjobStatus"
@@ -4054,12 +4030,12 @@ msgstr "Sezionamento non disponibile"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Slice current printjob"
-msgstr ""
+msgstr "Seziona processo di stampa corrente"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Cancel slicing process"
-msgstr ""
+msgstr "Annulla processo di slicing"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:183
msgctxt "@label:Printjob"
@@ -4074,7 +4050,7 @@ msgstr "Annulla"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:317
msgctxt "@info:tooltip"
msgid "Select the active output device"
-msgstr "Seleziona l'unità di uscita attiva"
+msgstr "Seleziona l'unità output attiva"
#: /home/ruben/Projects/Cura/resources/qml/OpenFilesIncludingProjectsDialog.qml:19
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:696
@@ -4115,7 +4091,7 @@ msgstr "Salva &come..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:139
msgctxt "@title:menu menubar:file"
msgid "Save &Project..."
-msgstr ""
+msgstr "Salva &progetto..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:162
msgctxt "@title:menu menubar:toplevel"
@@ -4344,7 +4320,7 @@ msgstr "Importa i modelli"
#: /home/ruben/Projects/Cura/resources/qml/EngineLog.qml:15
msgctxt "@title:window"
msgid "Engine Log"
-msgstr "Log motore"
+msgstr "Motore Log"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:245
msgctxt "@label"
@@ -4354,7 +4330,7 @@ msgstr "Materiale"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:352
msgctxt "@label"
msgid "Check compatibility"
-msgstr ""
+msgstr "Controlla compatibilità"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:372
msgctxt "@tooltip"
@@ -4364,17 +4340,17 @@ msgstr "Fai clic per verificare la compatibilità del materiale su Ultimaker.com
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:211
msgctxt "@option:check"
msgid "See only current build plate"
-msgstr ""
+msgstr "Vedi solo il piano di stampa corrente"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:227
msgctxt "@action:button"
msgid "Arrange to all build plates"
-msgstr ""
+msgstr "Sistema su tutti i piani di stampa"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:247
msgctxt "@action:button"
msgid "Arrange current build plate"
-msgstr ""
+msgstr "Sistema il piano di stampa corrente"
#: MachineSettingsAction/plugin.json
msgctxt "description"
@@ -4469,22 +4445,22 @@ msgstr "Stampa USB"
#: PrepareStage/plugin.json
msgctxt "description"
msgid "Provides a prepare stage in Cura."
-msgstr ""
+msgstr "Fornisce una fase di preparazione in Cura."
#: PrepareStage/plugin.json
msgctxt "name"
msgid "Prepare Stage"
-msgstr ""
+msgstr "Fase di preparazione"
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "description"
msgid "Provides an edit window for direct script editing."
-msgstr ""
+msgstr "Fornisce una finestra di modifica per la modifica script diretta."
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "name"
msgid "Live scripting tool"
-msgstr ""
+msgstr "Strumento di script diretto"
#: RemovableDriveOutputDevice/plugin.json
msgctxt "description"
@@ -4509,12 +4485,12 @@ msgstr "Connessione di rete UM3"
#: MonitorStage/plugin.json
msgctxt "description"
msgid "Provides a monitor stage in Cura."
-msgstr ""
+msgstr "Fornisce una fase di controllo in Cura."
#: MonitorStage/plugin.json
msgctxt "name"
msgid "Monitor Stage"
-msgstr ""
+msgstr "Fase di controllo"
#: FirmwareUpdateChecker/plugin.json
msgctxt "description"
@@ -4529,7 +4505,7 @@ msgstr "Controllo aggiornamento firmware"
#: CuraSolidWorksPlugin/plugin.json
msgctxt "description"
msgid "Gives you the possibility to open certain files using SolidWorks itself. Conversion is done by this plugin and additional optimizations."
-msgstr ""
+msgstr "Offre la possibilità di aprire alcuni file utilizzando SolidWorks. La conversione viene effettuata da questo plugin e ottimizzazioni addizionali."
#: CuraSolidWorksPlugin/plugin.json
msgctxt "name"
@@ -4599,12 +4575,12 @@ msgstr "Lettore legacy profilo Cura"
#: CuraBlenderPlugin/plugin.json
msgctxt "description"
msgid "Helps to open Blender files directly in Cura."
-msgstr ""
+msgstr "Aiuta ad aprire i file Blender direttamente in Cura."
#: CuraBlenderPlugin/plugin.json
msgctxt "name"
msgid "Blender Integration (experimental)"
-msgstr ""
+msgstr "Integrazione Blender (sperimentale)"
#: GCodeProfileReader/plugin.json
msgctxt "description"
@@ -4689,7 +4665,7 @@ msgstr "Lettore di immagine"
#: CuraEngineBackend/plugin.json
msgctxt "description"
msgid "Provides the link to the CuraEngine slicing backend."
-msgstr "Fornisce il collegamento al back-end di sezionamento CuraEngine."
+msgstr "Fornisce il collegamento al back-end di slicing di CuraEngine."
#: CuraEngineBackend/plugin.json
msgctxt "name"
@@ -4749,12 +4725,12 @@ msgstr "Visualizzazione compatta"
#: GCodeReader/plugin.json
msgctxt "description"
msgid "Allows loading and displaying G-code files."
-msgstr "Consente il caricamento e la visualizzazione dei file codice G."
+msgstr "Consente il caricamento e la visualizzazione dei file G-code."
#: GCodeReader/plugin.json
msgctxt "name"
msgid "G-code Reader"
-msgstr "Lettore codice G"
+msgstr "Lettore G-code"
#: CuraProfileWriter/plugin.json
msgctxt "description"
@@ -4769,12 +4745,12 @@ msgstr "Writer profilo Cura"
#: CuraPrintProfileCreator/plugin.json
msgctxt "description"
msgid "Allows material manufacturers to create new material and quality profiles using a drop-in UI."
-msgstr ""
+msgstr "Consente ai produttori di materiali di creare nuovi profili materiale e di qualità utilizzando una UI drop-in."
#: CuraPrintProfileCreator/plugin.json
msgctxt "name"
msgid "Print Profile Assistant"
-msgstr ""
+msgstr "Assistente profilo di stampa"
#: 3MFWriter/plugin.json
msgctxt "description"
@@ -4968,7 +4944,7 @@ msgstr "Lettore profilo Cura"
#~ msgctxt "@label:status"
#~ msgid "Blocked"
-#~ msgstr "Ostacolato"
+#~ msgstr "Bloccato"
#~ msgctxt "@label:status"
#~ msgid "Can't start print"
@@ -4988,7 +4964,7 @@ msgstr "Lettore profilo Cura"
#~ msgctxt "@info:title"
#~ msgid "Layer View"
-#~ msgstr "Visualizzazione strato"
+#~ msgstr "Visualizzazione layer"
#~ msgctxt "@menuitem"
#~ msgid "Browse plugins"
@@ -5092,7 +5068,7 @@ msgstr "Lettore profilo Cura"
#~ msgctxt "name"
#~ msgid "Layer View"
-#~ msgstr "Visualizzazione strato"
+#~ msgstr "Visualizzazione layer"
#~ msgctxt "@item:inlistbox"
#~ msgid "X-Ray"
@@ -5223,7 +5199,7 @@ msgstr "Lettore profilo Cura"
#~ msgctxt "@label"
#~ msgid "Hotend"
-#~ msgstr "Estremità calda"
+#~ msgstr "Hotend"
#~ msgctxt "@action:button"
#~ msgid "View Mode"
@@ -5359,7 +5335,7 @@ msgstr "Lettore profilo Cura"
#~ msgctxt "@label"
#~ msgid "The print cores and/or materials on your printer differ from those within your current project. For the best result, always slice for the print cores and materials that are inserted in your printer."
-#~ msgstr "I PrintCore e/o i materiali della stampante sono diversi da quelli del progetto corrente. Per ottenere i migliori risultati, sezionare sempre per i PrintCore e i materiali inseriti nella stampante utilizzata."
+#~ msgstr "I PrintCore e/o i materiali della stampante sono diversi da quelli del progetto corrente. Per risultati ottimali, sezionare sempre i PrintCore e i materiali inseriti nella stampante utilizzata."
#~ msgctxt "@label"
#~ msgid "Post Processing"
@@ -5415,11 +5391,11 @@ msgstr "Lettore profilo Cura"
#~ msgctxt "@label"
#~ msgid "Layer View"
-#~ msgstr "Visualizzazione strato"
+#~ msgstr "Visualizzazione layer"
#~ msgctxt "@info:whatsthis"
#~ msgid "Provides the Layer view."
-#~ msgstr "Fornisce la visualizzazione degli strati."
+#~ msgstr "Fornisce la visualizzazione dei layer."
#~ msgctxt "@label"
#~ msgid "Version Upgrade 2.5 to 2.6"
@@ -5459,7 +5435,7 @@ msgstr "Lettore profilo Cura"
#~ msgctxt "@info:whatsthis"
#~ msgid "Provides the link to the CuraEngine slicing backend."
-#~ msgstr "Fornisce il collegamento al back-end di sezionamento CuraEngine."
+#~ msgstr "Fornisce il collegamento al back-end di slicing di CuraEngine."
#~ msgctxt "@label"
#~ msgid "Per Model Settings Tool"
@@ -5487,11 +5463,11 @@ msgstr "Lettore profilo Cura"
#~ msgctxt "@label"
#~ msgid "G-code Reader"
-#~ msgstr "Lettore codice G"
+#~ msgstr "Lettore G-code"
#~ msgctxt "@info:whatsthis"
#~ msgid "Allows loading and displaying G-code files."
-#~ msgstr "Consente il caricamento e la visualizzazione dei file codice G."
+#~ msgstr "Consente il caricamento e la visualizzazione dei file G-code."
#~ msgctxt "@label"
#~ msgid "Cura Profile Writer"
@@ -5760,7 +5736,7 @@ msgstr "Lettore profilo Cura"
#~ msgctxt "@option:check"
#~ msgid "Only display top layer(s) in layer view"
-#~ msgstr "In visualizzazione strato, visualizza solo lo/gli strato/i superiore/i"
+#~ msgstr "In visualizzazione layer, visualizza solo il/i layer(s) superiore/i"
#~ msgctxt "@label"
#~ msgid "Opening files"
diff --git a/resources/i18n/it_IT/fdmprinter.def.json.po b/resources/i18n/it_IT/fdmprinter.def.json.po
index 204ebdc539..e5f440e2b2 100644
--- a/resources/i18n/it_IT/fdmprinter.def.json.po
+++ b/resources/i18n/it_IT/fdmprinter.def.json.po
@@ -1,15 +1,15 @@
-# Cura JSON setting files
-# Copyright (C) 2017 Ultimaker
+# Cura
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
"PO-Revision-Date: 2017-11-30 13:05+0100\n"
-"Last-Translator: Bothof \n"
+"Last-Translator: Crea-3D \n"
"Language-Team: Italian\n"
"Language: it_IT\n"
"MIME-Version: 1.0\n"
@@ -49,30 +49,26 @@ msgstr "Sceglie se mostrare le diverse varianti di questa macchina, descritte in
#: fdmprinter.def.json
msgctxt "machine_start_gcode label"
msgid "Start GCode"
-msgstr "Codice G avvio"
+msgstr "Avvio GCode"
#: fdmprinter.def.json
msgctxt "machine_start_gcode description"
msgid ""
"Gcode commands to be executed at the very start - separated by \n"
"."
-msgstr ""
-"I comandi codice G da eseguire all’avvio, separati da \n"
-"."
+msgstr "I comandi del Gcode da eseguire all’avvio, separati da \n."
#: fdmprinter.def.json
msgctxt "machine_end_gcode label"
msgid "End GCode"
-msgstr "Codice G fine"
+msgstr "Fine GCode"
#: fdmprinter.def.json
msgctxt "machine_end_gcode description"
msgid ""
"Gcode 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 del Gcode da eseguire alla fine, separati da \n."
#: fdmprinter.def.json
msgctxt "material_guid label"
@@ -92,7 +88,7 @@ msgstr "Attendi il riscaldamento del piano di stampa"
#: fdmprinter.def.json
msgctxt "material_bed_temp_wait description"
msgid "Whether to insert a command to wait until the build plate temperature is reached at the start."
-msgstr "Sceglie se inserire un comando per attendere finché la temperatura del piano di stampa non viene raggiunta all’avvio."
+msgstr "Scegli se inserire un comando per attendere finché la temperatura del piano di stampa non viene raggiunta all’avvio."
#: fdmprinter.def.json
msgctxt "material_print_temp_wait label"
@@ -102,7 +98,7 @@ msgstr "Attendi il riscaldamento dell’ugello"
#: fdmprinter.def.json
msgctxt "material_print_temp_wait description"
msgid "Whether to wait until the nozzle temperature is reached at the start."
-msgstr "Sceglie se attendere finché la temperatura dell’ugello non viene raggiunta all’avvio."
+msgstr "Scegli se attendere finché la temperatura dell’ugello non viene raggiunta all’avvio."
#: fdmprinter.def.json
msgctxt "material_print_temp_prepend label"
@@ -112,7 +108,7 @@ msgstr "Includi le temperature del materiale"
#: fdmprinter.def.json
msgctxt "material_print_temp_prepend description"
msgid "Whether to include nozzle temperature commands at the start of the gcode. When the start_gcode already contains nozzle temperature commands Cura frontend will automatically disable this setting."
-msgstr "Sceglie se includere comandi temperatura ugello all’avvio del codice G. Quando start_gcode contiene già comandi temperatura ugello la parte anteriore di Cura disabilita automaticamente questa impostazione."
+msgstr "Scegli se includere comandi temperatura ugello all’avvio del Gcode. Quando start_gcode contiene già comandi temperatura ugello, il frontend di Cura disabilita automaticamente questa impostazione."
#: fdmprinter.def.json
msgctxt "material_bed_temp_prepend label"
@@ -122,7 +118,7 @@ msgstr "Includi temperatura piano di stampa"
#: fdmprinter.def.json
msgctxt "material_bed_temp_prepend description"
msgid "Whether to include build plate temperature commands at the start of the gcode. When the start_gcode already contains build plate temperature commands Cura frontend will automatically disable this setting."
-msgstr "Sceglie se includere comandi temperatura piano di stampa all’avvio del codice G. Quando start_gcode contiene già comandi temperatura piano di stampa la parte anteriore di Cura disabilita automaticamente questa impostazione."
+msgstr "Scegli se includere comandi temperatura piano di stampa all’avvio del gcode. Quando start_gcode contiene già comandi temperatura piano di stampa il frontend di Cura disabilita automaticamente questa impostazione."
#: fdmprinter.def.json
msgctxt "machine_width label"
@@ -187,7 +183,7 @@ msgstr "Indica se la macchina ha un piano di stampa riscaldato."
#: fdmprinter.def.json
msgctxt "machine_center_is_zero label"
msgid "Is Center Origin"
-msgstr "Origine del centro"
+msgstr "Origine al centro"
#: fdmprinter.def.json
msgctxt "machine_center_is_zero description"
@@ -202,7 +198,7 @@ msgstr "Numero di estrusori"
#: fdmprinter.def.json
msgctxt "machine_extruder_count description"
msgid "Number of extruder trains. An extruder train is the combination of a feeder, bowden tube, and nozzle."
-msgstr "Il numero di treni di estrusori. Un treno di estrusori è la combinazione di un alimentatore, un tubo bowden e un ugello."
+msgstr "Il numero dei blocchi di estrusori. Un blocco di estrusori è la combinazione di un alimentatore, un tubo bowden e un ugello."
#: fdmprinter.def.json
msgctxt "machine_nozzle_tip_outer_diameter label"
@@ -222,7 +218,7 @@ msgstr "Lunghezza ugello"
#: fdmprinter.def.json
msgctxt "machine_nozzle_head_distance description"
msgid "The height difference between the tip of the nozzle and the lowest part of the print head."
-msgstr "La differenza di altezza tra la punta dell’ugello e la parte inferiore della testina di stampa."
+msgstr "La differenza di altezza tra la punta dell’ugello e la parte inferiore della testa di stampa."
#: fdmprinter.def.json
msgctxt "machine_nozzle_expansion_angle label"
@@ -297,12 +293,12 @@ msgstr "Il tempo minimo in cui un estrusore deve essere inattivo prima che l’u
#: fdmprinter.def.json
msgctxt "machine_gcode_flavor label"
msgid "Gcode flavour"
-msgstr "Tipo di codice G"
+msgstr "Tipo di Gcode"
#: fdmprinter.def.json
msgctxt "machine_gcode_flavor description"
msgid "The type of gcode to be generated."
-msgstr "Il tipo di codice G da generare."
+msgstr "Il tipo di gcode da generare."
#: fdmprinter.def.json
msgctxt "machine_gcode_flavor option RepRap (Marlin/Sprinter)"
@@ -352,12 +348,12 @@ msgstr "Repetier"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract label"
msgid "Firmware Retraction"
-msgstr ""
+msgstr "Retrazione firmware"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract description"
msgid "Whether to use firmware retract commands (G10/G11) instead of using the E property in G1 commands to retract the material."
-msgstr ""
+msgstr "Specifica se usare comandi di retrazione firmware (G10/G11) anziché utilizzare la proprietà E nei comandi G1 per retrarre il materiale."
#: fdmprinter.def.json
msgctxt "machine_disallowed_areas label"
@@ -367,7 +363,7 @@ msgstr "Aree non consentite"
#: fdmprinter.def.json
msgctxt "machine_disallowed_areas description"
msgid "A list of polygons with areas the print head is not allowed to enter."
-msgstr "Un elenco di poligoni con aree alle quali la testina di stampa non può accedere."
+msgstr "Un elenco di poligoni con aree alle quali la testa di stampa non può accedere."
#: fdmprinter.def.json
msgctxt "nozzle_disallowed_areas label"
@@ -382,22 +378,22 @@ msgstr "Un elenco di poligoni con aree alle quali l’ugello non può accedere."
#: fdmprinter.def.json
msgctxt "machine_head_polygon label"
msgid "Machine head polygon"
-msgstr "Poligono testina macchina"
+msgstr "Poligono testa macchina"
#: fdmprinter.def.json
msgctxt "machine_head_polygon description"
msgid "A 2D silhouette of the print head (fan caps excluded)."
-msgstr "Una silhouette 2D della testina di stampa (cappucci ventola esclusi)."
+msgstr "Una silhouette 2D della testa di stampa (coperture ventola escluse)."
#: fdmprinter.def.json
msgctxt "machine_head_with_fans_polygon label"
msgid "Machine head & Fan polygon"
-msgstr "Poligono testina macchina e ventola"
+msgstr "Poligono testa macchina e ventola"
#: fdmprinter.def.json
msgctxt "machine_head_with_fans_polygon description"
msgid "A 2D silhouette of the print head (fan caps included)."
-msgstr "Una silhouette 2D della testina di stampa (cappucci ventola inclusi)."
+msgstr "Una silhouette 2D della testa di stampa (coperture ventola incluse)."
#: fdmprinter.def.json
msgctxt "gantry_height label"
@@ -417,7 +413,7 @@ msgstr "ID ugello"
#: fdmprinter.def.json
msgctxt "machine_nozzle_id description"
msgid "The nozzle ID for an extruder train, such as \"AA 0.4\" and \"BB 0.8\"."
-msgstr "ID ugello per un treno estrusore, come \"AA 0.4\" e \"BB 0.8\"."
+msgstr "ID ugello per un blocco estrusore, come \"AA 0.4\" e \"BB 0.8\"."
#: fdmprinter.def.json
msgctxt "machine_nozzle_size label"
@@ -597,27 +593,27 @@ msgstr "Qualità"
#: fdmprinter.def.json
msgctxt "resolution description"
msgid "All settings that influence the resolution of the print. These settings have a large impact on the quality (and print time)"
-msgstr "Indica tutte le impostazioni che influiscono sulla risoluzione della stampa. Queste impostazioni hanno un elevato impatto sulla qualità (e il tempo di stampa)"
+msgstr "Indica tutte le impostazioni che influiscono sulla risoluzione della stampa. Queste impostazioni hanno un elevato impatto sulla qualità (e sul tempo di stampa)"
#: fdmprinter.def.json
msgctxt "layer_height label"
msgid "Layer Height"
-msgstr "Altezza dello strato"
+msgstr "Altezza del layer"
#: fdmprinter.def.json
msgctxt "layer_height description"
msgid "The height of each layer in mm. Higher values produce faster prints in lower resolution, lower values produce slower prints in higher resolution."
-msgstr "Indica l’altezza di ciascuno strato in mm. Valori più elevati generano stampe più rapide con risoluzione inferiore, valori più bassi generano stampe più lente con risoluzione superiore."
+msgstr "Indica l’altezza di ciascun layer in mm. Valori più elevati generano stampe più rapide con risoluzione inferiore, valori più bassi generano stampe più lente con risoluzione superiore."
#: fdmprinter.def.json
msgctxt "layer_height_0 label"
msgid "Initial Layer Height"
-msgstr "Altezza dello strato iniziale"
+msgstr "Altezza iniziale del layer"
#: fdmprinter.def.json
msgctxt "layer_height_0 description"
msgid "The height of the initial layer in mm. A thicker initial layer makes adhesion to the build plate easier."
-msgstr "Indica l’altezza dello strato iniziale in mm. Uno strato iniziale più spesso facilita l’adesione al piano di stampa."
+msgstr "Indica l’altezza del layer iniziale in mm. Un layer iniziale più spesso facilita l’adesione al piano di stampa."
#: fdmprinter.def.json
msgctxt "line_width label"
@@ -737,17 +733,17 @@ msgstr "Larghezza della linea della torre di innesco"
#: fdmprinter.def.json
msgctxt "prime_tower_line_width description"
msgid "Width of a single prime tower line."
-msgstr "Indica la larghezza di una singola linea della torre di innesco."
+msgstr "Indica la larghezza di una singola linea della prime tower."
#: fdmprinter.def.json
msgctxt "initial_layer_line_width_factor label"
msgid "Initial Layer Line Width"
-msgstr "Larghezza linea strato iniziale"
+msgstr "Larghezza linea layer iniziale"
#: fdmprinter.def.json
msgctxt "initial_layer_line_width_factor description"
msgid "Multiplier of the line width on the first layer. Increasing this could improve bed adhesion."
-msgstr "Moltiplicatore della larghezza della linea del primo strato Il suo aumento potrebbe migliorare l'adesione al piano"
+msgstr "Moltiplicatore della larghezza della linea del primo layer. Il suo aumento potrebbe migliorare l'adesione al piano"
#: fdmprinter.def.json
msgctxt "shell label"
@@ -767,7 +763,7 @@ msgstr "Estrusore pareti"
#: fdmprinter.def.json
msgctxt "wall_extruder_nr description"
msgid "The extruder train used for printing the walls. This is used in multi-extrusion."
-msgstr "Treno estrusore utilizzato per stampare le pareti. Si utilizza nell'estrusione multipla."
+msgstr "Blocco estrusore utilizzato per stampare le pareti. Si utilizza nell'estrusione multipla."
#: fdmprinter.def.json
msgctxt "wall_0_extruder_nr label"
@@ -777,7 +773,7 @@ msgstr "Estrusore parete esterna"
#: fdmprinter.def.json
msgctxt "wall_0_extruder_nr description"
msgid "The extruder train used for printing the outer wall. This is used in multi-extrusion."
-msgstr "Treno estrusore utilizzato per stampare la parete esterna. Si utilizza nell'estrusione multipla."
+msgstr "Blocco estrusore utilizzato per stampare la parete esterna. Si utilizza nell'estrusione multipla."
#: fdmprinter.def.json
msgctxt "wall_x_extruder_nr label"
@@ -787,7 +783,7 @@ msgstr "Estrusore parete interna"
#: fdmprinter.def.json
msgctxt "wall_x_extruder_nr description"
msgid "The extruder train used for printing the inner walls. This is used in multi-extrusion."
-msgstr "Treno estrusore utilizzato per stampare le pareti interne. Si utilizza nell'estrusione multipla."
+msgstr "Blocco estrusore utilizzato per stampare le pareti interne. Si utilizza nell'estrusione multipla."
#: fdmprinter.def.json
msgctxt "wall_thickness label"
@@ -807,7 +803,7 @@ msgstr "Numero delle linee perimetrali"
#: fdmprinter.def.json
msgctxt "wall_line_count description"
msgid "The number of walls. When calculated by the wall thickness, this value is rounded to a whole number."
-msgstr "Indica il numero delle pareti. Quando calcolato mediante lo spessore della parete, il valore viene arrotondato a numero intero."
+msgstr "Indica il numero delle pareti. Se calcolato mediante lo spessore della parete, il valore viene arrotondato a numero intero."
#: fdmprinter.def.json
msgctxt "wall_0_wipe_dist label"
@@ -827,17 +823,17 @@ msgstr "Estrusore rivestimento superficie superiore"
#: fdmprinter.def.json
msgctxt "roofing_extruder_nr description"
msgid "The extruder train used for printing the top most skin. This is used in multi-extrusion."
-msgstr "Treno estrusore utilizzato per stampare il rivestimento più in alto. Si utilizza nell'estrusione multipla."
+msgstr "Blocco estrusore utilizzato per stampare il rivestimento più in alto. Si utilizza nell'estrusione multipla."
#: fdmprinter.def.json
msgctxt "roofing_layer_count label"
msgid "Top Surface Skin Layers"
-msgstr "Strati di rivestimento superficie superiore"
+msgstr "Layer di rivestimento superficie superiore"
#: fdmprinter.def.json
msgctxt "roofing_layer_count description"
msgid "The number of top most skin layers. Usually only one top most layer is sufficient to generate higher quality top surfaces."
-msgstr "Numero degli strati di rivestimento superiori. Solitamente è sufficiente un unico strato di sommità per ottenere superfici superiori di qualità elevata."
+msgstr "Numero dei layers di rivestimento superiori. Solitamente è sufficiente un unico layer di sommità per ottenere superfici superiori di qualità elevata."
#: fdmprinter.def.json
msgctxt "top_bottom_extruder_nr label"
@@ -847,67 +843,67 @@ msgstr "Estrusore superiore/inferiore"
#: fdmprinter.def.json
msgctxt "top_bottom_extruder_nr description"
msgid "The extruder train used for printing the top and bottom skin. This is used in multi-extrusion."
-msgstr "Treno estrusore utilizzato per stampare il rivestimento superiore e quello inferiore. Si utilizza nell'estrusione multipla."
+msgstr "Blocco estrusore utilizzato per stampare il rivestimento superiore e quello inferiore. Si utilizza nell'estrusione multipla."
#: fdmprinter.def.json
msgctxt "top_bottom_thickness label"
msgid "Top/Bottom Thickness"
-msgstr "Spessore dello strato superiore/inferiore"
+msgstr "Spessore del layer superiore/inferiore"
#: fdmprinter.def.json
msgctxt "top_bottom_thickness description"
msgid "The thickness of the top/bottom layers in the print. This value divided by the layer height defines the number of top/bottom layers."
-msgstr "Indica lo spessore degli strati superiore/inferiore nella stampa. Questo valore diviso per la l’altezza dello strato definisce il numero degli strati superiori/inferiori."
+msgstr "Indica lo spessore dei layers superiore/inferiore nella stampa. Questo valore diviso per la l’altezza del layer definisce il numero dei layers superiori/inferiori."
#: fdmprinter.def.json
msgctxt "top_thickness label"
msgid "Top Thickness"
-msgstr "Spessore dello strato superiore"
+msgstr "Spessore del layer superiore"
#: fdmprinter.def.json
msgctxt "top_thickness description"
msgid "The thickness of the top layers in the print. This value divided by the layer height defines the number of top layers."
-msgstr "Indica lo spessore degli strati superiori nella stampa. Questo valore diviso per la l’altezza dello strato definisce il numero degli strati superiori."
+msgstr "Indica lo spessore dei layers superiori nella stampa. Questo valore diviso per la l’altezza del layer definisce il numero dei layers superiori."
#: fdmprinter.def.json
msgctxt "top_layers label"
msgid "Top Layers"
-msgstr "Strati superiori"
+msgstr "Layers superiori"
#: fdmprinter.def.json
msgctxt "top_layers description"
msgid "The number of top layers. When calculated by the top thickness, this value is rounded to a whole number."
-msgstr "Indica il numero degli strati superiori. Quando calcolato mediante lo spessore dello strato superiore, il valore viene arrotondato a numero intero."
+msgstr "Indica il numero dei layers superiori. Se calcolato mediante lo spessore del layer superiore, il valore viene arrotondato a numero intero."
#: fdmprinter.def.json
msgctxt "bottom_thickness label"
msgid "Bottom Thickness"
-msgstr "Spessore degli strati inferiori"
+msgstr "Spessore dei layers inferiori"
#: fdmprinter.def.json
msgctxt "bottom_thickness description"
msgid "The thickness of the bottom layers in the print. This value divided by the layer height defines the number of bottom layers."
-msgstr "Indica lo spessore degli strati inferiori nella stampa. Questo valore diviso per la l’altezza dello strato definisce il numero degli strati inferiori."
+msgstr "Indica lo spessore dei layers inferiori nella stampa. Questo valore diviso per la l’altezza del layer definisce il numero dei layers inferiori."
#: fdmprinter.def.json
msgctxt "bottom_layers label"
msgid "Bottom Layers"
-msgstr "Strati inferiori"
+msgstr "Layers inferiori"
#: fdmprinter.def.json
msgctxt "bottom_layers description"
msgid "The number of bottom layers. When calculated by the bottom thickness, this value is rounded to a whole number."
-msgstr "Indica il numero degli strati inferiori. Quando calcolato mediante lo spessore dello strato inferiore, il valore viene arrotondato a numero intero."
+msgstr "Indica il numero dei layers inferiori. Quando calcolato mediante lo spessore del layer inferiore, il valore viene arrotondato a numero intero."
#: fdmprinter.def.json
msgctxt "top_bottom_pattern label"
msgid "Top/Bottom Pattern"
-msgstr "Configurazione dello strato superiore/inferiore"
+msgstr "Configurazione del layer superiore/inferiore"
#: fdmprinter.def.json
msgctxt "top_bottom_pattern description"
msgid "The pattern of the top/bottom layers."
-msgstr "Indica la configurazione degli strati superiori/inferiori."
+msgstr "Indica la configurazione dei layers superiori/inferiori."
#: fdmprinter.def.json
msgctxt "top_bottom_pattern option lines"
@@ -927,12 +923,12 @@ msgstr "Zig Zag"
#: fdmprinter.def.json
msgctxt "top_bottom_pattern_0 label"
msgid "Bottom Pattern Initial Layer"
-msgstr "Strato iniziale configurazione inferiore"
+msgstr "Layer iniziale configurazione inferiore"
#: fdmprinter.def.json
msgctxt "top_bottom_pattern_0 description"
msgid "The pattern on the bottom of the print on the first layer."
-msgstr "La configurazione al fondo della stampa sul primo strato."
+msgstr "La configurazione al fondo della stampa sul primo layer."
#: fdmprinter.def.json
msgctxt "top_bottom_pattern_0 option lines"
@@ -957,7 +953,7 @@ msgstr "Direzioni delle linee superiori/inferiori"
#: fdmprinter.def.json
msgctxt "skin_angles description"
msgid "A list of integer line directions to use when the top/bottom layers use the lines or zig zag pattern. 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)."
-msgstr "Un elenco di direzioni linee intere da usare quando gli strati superiori/inferiori utilizzano le linee o la configurazione zig zag. Gli elementi dall’elenco sono utilizzati in sequenza con il progredire degli strati e, al raggiungimento della fine dell’elenco, la sequenza ricomincia dall’inizio. Le voci elencate sono separate da virgole e l’intero elenco è racchiuso tra parentesi quadre. L’elenco predefinito è vuoto, vale a dire che utilizza i valori angolari predefiniti (45 e 135 gradi)."
+msgstr "Un elenco di direzioni linee intere da usare quando i layers superiori/inferiori utilizzano le linee o la configurazione zig zag. Gli elementi dall’elenco sono utilizzati in sequenza con il progredire dei layers e, al raggiungimento della fine dell’elenco, la sequenza ricomincia dall’inizio. Le voci elencate sono separate da virgole e l’intero elenco è racchiuso tra parentesi quadre. L’elenco predefinito è vuoto, vale a dire che utilizza i valori angolari predefiniti (45 e 135 gradi)."
#: fdmprinter.def.json
msgctxt "wall_0_inset label"
@@ -1052,12 +1048,12 @@ msgstr "In tutti i possibili punti"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps label"
msgid "Filter Out Tiny Gaps"
-msgstr ""
+msgstr "Esclusione spazi minimi"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps description"
msgid "Filter out tiny gaps to reduce blobs on outside of model."
-msgstr ""
+msgstr "Esclude gli spazi minimi per ridurre le gocce sull’esterno del modello."
#: fdmprinter.def.json
msgctxt "fill_outline_gaps label"
@@ -1077,17 +1073,17 @@ msgstr "Espansione orizzontale"
#: fdmprinter.def.json
msgctxt "xy_offset description"
msgid "Amount of offset applied to all polygons in each layer. Positive values can compensate for too big holes; negative values can compensate for too small holes."
-msgstr "Determina l'entità di offset (o estensione dello strato) applicata a tutti i poligoni su ciascuno strato. I valori positivi possono compensare fori troppo estesi; i valori negativi possono compensare fori troppo piccoli."
+msgstr "Determina l'entità di offset (o estensione del layer) applicata a tutti i poligoni su ciascun layer. I valori positivi possono compensare fori troppo estesi; i valori negativi possono compensare fori troppo piccoli."
#: fdmprinter.def.json
msgctxt "xy_offset_layer_0 label"
msgid "Initial Layer Horizontal Expansion"
-msgstr "Espansione orizzontale dello strato iniziale"
+msgstr "Espansione orizzontale del layer iniziale"
#: fdmprinter.def.json
msgctxt "xy_offset_layer_0 description"
msgid "Amount of offset applied to all polygons in the first layer. A negative value can compensate for squishing of the first layer known as \"elephant's foot\"."
-msgstr "È l'entità di offset (estensione dello strato) applicata a tutti i poligoni di supporto in ciascuno strato. Un valore negativo può compensare lo schiacciamento del primo strato noto come \"zampa di elefante\"."
+msgstr "È l'entità di offset (estensione del layer) applicata a tutti i poligoni di supporto in ciascun layer. Un valore negativo può compensare lo schiacciamento del primo layer noto come \"zampa di elefante\"."
#: fdmprinter.def.json
msgctxt "z_seam_type label"
@@ -1097,7 +1093,7 @@ msgstr "Allineamento delle giunzioni a Z"
#: fdmprinter.def.json
msgctxt "z_seam_type description"
msgid "Starting point of each path in a layer. When paths in consecutive layers start at the same point a vertical seam may show on the print. When aligning these near a user specified location, the seam is easiest to remove. When placed randomly the inaccuracies at the paths' start will be less noticeable. When taking the shortest path the print will be quicker."
-msgstr "Punto di partenza di ogni percorso nell'ambito di uno strato. Quando i percorsi in strati consecutivi iniziano nello stesso punto, sulla stampa può apparire una linea di giunzione verticale. Se si allineano in prossimità di una posizione specificata dall’utente, la linea di giunzione può essere rimossa più facilmente. Se disposti in modo casuale, le imprecisioni in corrispondenza dell'inizio del percorso saranno meno evidenti. Prendendo il percorso più breve la stampa sarà più veloce."
+msgstr "Punto di partenza di ogni percorso nell'ambito di un layer. Quando i percorsi in layers consecutivi iniziano nello stesso punto, sulla stampa può apparire una linea di giunzione verticale. Se si allineano in prossimità di una posizione specificata dall’utente, la linea di giunzione può essere rimossa più facilmente. Se disposti in modo casuale, le imprecisioni in corrispondenza dell'inizio del percorso saranno meno evidenti. Prendendo il percorso più breve la stampa sarà più veloce."
#: fdmprinter.def.json
msgctxt "z_seam_type option back"
@@ -1127,7 +1123,7 @@ msgstr "Giunzione Z X"
#: fdmprinter.def.json
msgctxt "z_seam_x description"
msgid "The X coordinate of the position near where to start printing each part in a layer."
-msgstr "La coordinata X della posizione in prossimità della quale si innesca all’avvio della stampa di ciascuna parte in uno strato."
+msgstr "La coordinata X della posizione in prossimità della quale si innesca all’avvio della stampa di ciascuna parte in un layer."
#: fdmprinter.def.json
msgctxt "z_seam_y label"
@@ -1137,7 +1133,7 @@ msgstr "Giunzione Z Y"
#: fdmprinter.def.json
msgctxt "z_seam_y description"
msgid "The Y coordinate of the position near where to start printing each part in a layer."
-msgstr "La coordinata Y della posizione in prossimità della quale si innesca all’avvio della stampa di ciascuna parte in uno strato."
+msgstr "La coordinata Y della posizione in prossimità della quale si innesca all’avvio della stampa di ciascuna parte in un layer."
#: fdmprinter.def.json
msgctxt "z_seam_corner label"
@@ -1212,12 +1208,12 @@ msgstr "Ulteriore passaggio sopra la superficie superiore, senza estrusione di m
#: fdmprinter.def.json
msgctxt "ironing_only_highest_layer label"
msgid "Iron Only Highest Layer"
-msgstr "Stiramento del solo strato più elevato"
+msgstr "Stiramento del solo layer più elevato"
#: fdmprinter.def.json
msgctxt "ironing_only_highest_layer description"
msgid "Only perform ironing on the very last layer of the mesh. This saves time if the lower layers don't need a smooth surface finish."
-msgstr "Effettua lo stiramento solo dell'ultimissimo strato della maglia. È possibile quindi risparmiare tempo se gli strati inferiori non richiedono una finitura con superficie liscia."
+msgstr "Effettua lo stiramento solo dell'ultimissimo layer della maglia. È possibile quindi risparmiare tempo se i layers inferiori non richiedono una finitura con superficie liscia."
#: fdmprinter.def.json
msgctxt "ironing_pattern label"
@@ -1317,7 +1313,7 @@ msgstr "Estrusore riempimento"
#: fdmprinter.def.json
msgctxt "infill_extruder_nr description"
msgid "The extruder train used for printing infill. This is used in multi-extrusion."
-msgstr "Treno estrusore utilizzato per stampare il riempimento. Si utilizza nell'estrusione multipla."
+msgstr "Blocco estrusore utilizzato per stampare il riempimento. Si utilizza nell'estrusione multipla."
#: fdmprinter.def.json
msgctxt "infill_sparse_density label"
@@ -1347,7 +1343,7 @@ msgstr "Configurazione di riempimento"
#: fdmprinter.def.json
msgctxt "infill_pattern description"
msgid "The pattern of the infill material of the print. The line and zig zag infill swap direction on alternate layers, reducing material cost. The grid, triangle, tri-hexagon, cubic, octet, quarter cubic, cross and concentric patterns are fully printed every layer. Cubic, quarter cubic and octet infill change with every layer to provide a more equal distribution of strength over each direction."
-msgstr "Configurazione del materiale di riempimento della stampa. Il riempimento a linea e a zig zag cambia direzione su strati alternati, riducendo il costo del materiale. Le configurazioni a griglia, a triangolo, tri-esagonali, cubiche, ottagonali, a quarto di cubo, incrociate e concentriche sono stampate completamente su ogni strato. Le configurazioni cubiche, a quarto di cubo e ottagonali variano per ciascuno strato per garantire una più uniforme distribuzione della forza in ogni direzione."
+msgstr "Configurazione del materiale di riempimento della stampa. Il riempimento a linea e a zig zag cambia direzione su strati alternati, riducendo il costo del materiale. Le configurazioni a griglia, a triangolo, tri-esagonali, cubiche, ottagonali, a quarto di cubo, incrociate e concentriche sono stampate completamente su ogni layer. Le configurazioni cubiche, a quarto di cubo e ottagonali variano per ciascun layer per garantire una più uniforme distribuzione della forza in ogni direzione."
#: fdmprinter.def.json
msgctxt "infill_pattern option grid"
@@ -1432,7 +1428,7 @@ msgstr "Direzioni delle linee di riempimento"
#: fdmprinter.def.json
msgctxt "infill_angles description"
msgid "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)."
-msgstr "Un elenco di direzioni linee intere. Gli elementi dall’elenco sono utilizzati in sequenza con il progredire degli strati e, al raggiungimento della fine dell’elenco, la sequenza ricomincia dall’inizio. Le voci elencate sono separate da virgole e l’intero elenco è racchiuso tra parentesi quadre. L’elenco predefinito è vuoto, vale a dire che utilizza i valori angolari predefiniti (45 e 135 gradi per le linee e la configurazione zig zag e 45 gradi per tutte le altre configurazioni)."
+msgstr "Un elenco di direzioni linee intere. Gli elementi dall’elenco sono utilizzati in sequenza con il progredire dei layers e, al raggiungimento della fine dell’elenco, la sequenza ricomincia dall’inizio. Le voci elencate sono separate da virgole e l’intero elenco è racchiuso tra parentesi quadre. L’elenco predefinito è vuoto, vale a dire che utilizza i valori angolari predefiniti (45 e 135 gradi per le linee e la configurazione zig zag e 45 gradi per tutte le altre configurazioni)."
#: fdmprinter.def.json
msgctxt "infill_offset_x label"
@@ -1442,7 +1438,7 @@ msgstr "Offset X riempimento"
#: fdmprinter.def.json
msgctxt "infill_offset_x description"
msgid "The infill pattern is moved this distance along the X axis."
-msgstr ""
+msgstr "Il riempimento si sposta di questa distanza lungo l'asse X."
#: fdmprinter.def.json
msgctxt "infill_offset_y label"
@@ -1452,7 +1448,7 @@ msgstr "Offset Y riempimento"
#: fdmprinter.def.json
msgctxt "infill_offset_y description"
msgid "The infill pattern is moved this distance along the Y axis."
-msgstr ""
+msgstr "Il riempimento si sposta di questa distanza lungo l'asse Y."
#: fdmprinter.def.json
msgctxt "sub_div_rad_add label"
@@ -1472,7 +1468,7 @@ msgstr "Percentuale di sovrapposizione del riempimento"
#: fdmprinter.def.json
msgctxt "infill_overlap description"
msgid "The amount of overlap between the infill and the walls as a percentage of the infill line width. A slight overlap allows the walls to connect firmly to the infill."
-msgstr ""
+msgstr "Indica la quantità di sovrapposizione tra il riempimento e le pareti come percentuale della larghezza della linea di riempimento. Una leggera sovrapposizione consente il saldo collegamento delle pareti al riempimento."
#: fdmprinter.def.json
msgctxt "infill_overlap_mm label"
@@ -1492,7 +1488,7 @@ msgstr "Percentuale di sovrapposizione del rivestimento esterno"
#: fdmprinter.def.json
msgctxt "skin_overlap description"
msgid "The amount of overlap between the skin and the walls as a percentage of the skin line width. A slight overlap allows the walls to connect firmly to the skin. This is a percentage of the average line widths of the skin lines and the innermost wall."
-msgstr ""
+msgstr "Entità della sovrapposizione tra il rivestimento e le pareti espressa in percentuale della larghezza della linea del rivestimento esterno. Una leggera sovrapposizione consente alle pareti di essere saldamente collegate al rivestimento. È una percentuale delle larghezze medie delle linee del rivestimento e della parete più interna."
#: fdmprinter.def.json
msgctxt "skin_overlap_mm label"
@@ -1517,12 +1513,12 @@ msgstr "Indica la distanza di uno spostamento inserito dopo ogni linea di riempi
#: fdmprinter.def.json
msgctxt "infill_sparse_thickness label"
msgid "Infill Layer Thickness"
-msgstr "Spessore dello strato di riempimento"
+msgstr "Spessore del layer di riempimento"
#: fdmprinter.def.json
msgctxt "infill_sparse_thickness description"
msgid "The thickness per layer of infill material. This value should always be a multiple of the layer height and is otherwise rounded."
-msgstr "Indica lo spessore per strato di materiale di riempimento. Questo valore deve sempre essere un multiplo dell’altezza dello strato e in caso contrario viene arrotondato."
+msgstr "Indica lo spessore per layer di materiale di riempimento. Questo valore deve sempre essere un multiplo dell’altezza del layer e in caso contrario viene arrotondato."
#: fdmprinter.def.json
msgctxt "gradual_infill_steps label"
@@ -1597,7 +1593,7 @@ msgstr "Larghezza massima delle aree di rivestimento inferiore che è possibile
#: fdmprinter.def.json
msgctxt "expand_skins_expand_distance label"
msgid "Skin Expand Distance"
-msgstr "Distanza prolunga rivestimento esterno"
+msgstr "Distanza espansione rivestimento esterno"
#: fdmprinter.def.json
msgctxt "expand_skins_expand_distance description"
@@ -1607,7 +1603,7 @@ msgstr "Distanza per cui i rivestimenti si estendono nel riempimento. Valori mag
#: fdmprinter.def.json
msgctxt "top_skin_expand_distance label"
msgid "Top Skin Expand Distance"
-msgstr "Distanza prolunga rivestimento superiore"
+msgstr "Distanza espansione rivestimento superiore"
#: fdmprinter.def.json
msgctxt "top_skin_expand_distance description"
@@ -1617,7 +1613,7 @@ msgstr "Distanza per cui i rivestimenti superiori si estendono nel riempimento.
#: fdmprinter.def.json
msgctxt "bottom_skin_expand_distance label"
msgid "Bottom Skin Expand Distance"
-msgstr "Distanza prolunga rivestimento inferiore"
+msgstr "Distanza espansione rivestimento inferiore"
#: fdmprinter.def.json
msgctxt "bottom_skin_expand_distance description"
@@ -1627,7 +1623,7 @@ msgstr "Distanza per cui i rivestimenti inferiori si estendono nel riempimento.
#: fdmprinter.def.json
msgctxt "max_skin_angle_for_expansion label"
msgid "Maximum Skin Angle for Expansion"
-msgstr "Angolo massimo rivestimento esterno per prolunga"
+msgstr "Angolo massimo rivestimento esterno per espansione"
#: fdmprinter.def.json
msgctxt "max_skin_angle_for_expansion description"
@@ -1637,7 +1633,7 @@ msgstr "Per le superfici inferiori e/o superiori dell’oggetto con un angolo ma
#: fdmprinter.def.json
msgctxt "min_skin_width_for_expansion label"
msgid "Minimum Skin Width for Expansion"
-msgstr "Larghezza minima rivestimento esterno per prolunga"
+msgstr "Larghezza minima rivestimento esterno per espansione"
#: fdmprinter.def.json
msgctxt "min_skin_width_for_expansion description"
@@ -1677,12 +1673,12 @@ msgstr "Indica la temperatura usata per la stampa."
#: fdmprinter.def.json
msgctxt "material_print_temperature_layer_0 label"
msgid "Printing Temperature Initial Layer"
-msgstr "Temperatura di stampa Strato iniziale"
+msgstr "Temperatura di stampa layer iniziale"
#: fdmprinter.def.json
msgctxt "material_print_temperature_layer_0 description"
msgid "The temperature used for printing the first layer. Set at 0 to disable special handling of the initial layer."
-msgstr "Indica la temperatura usata per la stampa del primo strato. Impostare a 0 per disabilitare la manipolazione speciale dello strato iniziale."
+msgstr "Indica la temperatura usata per la stampa del primo layer. Impostare a 0 per disabilitare la manipolazione speciale del layer iniziale."
#: fdmprinter.def.json
msgctxt "material_initial_print_temperature label"
@@ -1722,17 +1718,17 @@ msgstr "Temperatura piano di stampa"
#: fdmprinter.def.json
msgctxt "material_bed_temperature description"
msgid "The temperature used for the heated build plate. If this is 0, the bed temperature will not be adjusted."
-msgstr ""
+msgstr "Indica la temperatura usata per il piano di stampa riscaldato. Se è 0, la temperatura del piano non si regola."
#: fdmprinter.def.json
msgctxt "material_bed_temperature_layer_0 label"
msgid "Build Plate Temperature Initial Layer"
-msgstr "Temperatura piano di stampa Strato iniziale"
+msgstr "Temperatura piano di stampa Layer iniziale"
#: fdmprinter.def.json
msgctxt "material_bed_temperature_layer_0 description"
msgid "The temperature used for the heated build plate at the first layer."
-msgstr "Indica la temperatura usata per il piano di stampa riscaldato per il primo strato."
+msgstr "Indica la temperatura usata per il piano di stampa riscaldato per il primo layer."
#: fdmprinter.def.json
msgctxt "material_diameter label"
@@ -1787,12 +1783,12 @@ msgstr "Ritrae il filamento quando l'ugello si sta muovendo su un'area non stamp
#: fdmprinter.def.json
msgctxt "retract_at_layer_change label"
msgid "Retract at Layer Change"
-msgstr "Retrazione al cambio strato"
+msgstr "Retrazione al cambio layer"
#: fdmprinter.def.json
msgctxt "retract_at_layer_change description"
msgid "Retract the filament when the nozzle is moving to the next layer."
-msgstr "Ritrae il filamento quando l'ugello si sta muovendo allo strato successivo. "
+msgstr "Ritrae il filamento quando l'ugello si sta muovendo al layer successivo. "
#: fdmprinter.def.json
msgctxt "retraction_amount label"
@@ -2057,12 +2053,12 @@ msgstr "Velocità alla quale viene stampata la parte inferiore del supporto. La
#: fdmprinter.def.json
msgctxt "speed_prime_tower label"
msgid "Prime Tower Speed"
-msgstr "Velocità della torre di innesco"
+msgstr "Velocità della Prime Tower"
#: fdmprinter.def.json
msgctxt "speed_prime_tower description"
msgid "The speed at which the prime tower is printed. Printing the prime tower slower can make it more stable when the adhesion between the different filaments is suboptimal."
-msgstr "Indica la velocità alla quale è stampata la torre di innesco. La stampa della torre di innesco a una velocità inferiore può renderla maggiormente stabile quando l’adesione tra i diversi filamenti non è ottimale."
+msgstr "Indica la velocità alla quale è stampata la Prime Tower. La stampa della Prime Tower a una velocità inferiore può renderla maggiormente stabile quando l’adesione tra i diversi filamenti non è ottimale."
#: fdmprinter.def.json
msgctxt "speed_travel label"
@@ -2077,32 +2073,32 @@ msgstr "Indica la velocità alla quale vengono effettuati gli spostamenti."
#: fdmprinter.def.json
msgctxt "speed_layer_0 label"
msgid "Initial Layer Speed"
-msgstr "Velocità di stampa dello strato iniziale"
+msgstr "Velocità di stampa del layer iniziale"
#: fdmprinter.def.json
msgctxt "speed_layer_0 description"
msgid "The speed for the initial layer. A lower value is advised to improve adhesion to the build plate."
-msgstr "Indica la velocità per lo strato iniziale. Un valore inferiore è consigliabile per migliorare l’adesione al piano di stampa."
+msgstr "Indica la velocità per il layer iniziale. Un valore inferiore è consigliabile per migliorare l’adesione al piano di stampa."
#: fdmprinter.def.json
msgctxt "speed_print_layer_0 label"
msgid "Initial Layer Print Speed"
-msgstr "Velocità di stampa strato iniziale"
+msgstr "Velocità di stampa layer iniziale"
#: fdmprinter.def.json
msgctxt "speed_print_layer_0 description"
msgid "The speed of printing for the initial layer. A lower value is advised to improve adhesion to the build plate."
-msgstr "Indica la velocità di stampa per lo strato iniziale. Un valore inferiore è consigliabile per migliorare l’adesione al piano di stampa."
+msgstr "Indica la velocità di stampa per il layer iniziale. Un valore inferiore è consigliabile per migliorare l’adesione al piano di stampa."
#: fdmprinter.def.json
msgctxt "speed_travel_layer_0 label"
msgid "Initial Layer Travel Speed"
-msgstr "Velocità di spostamento dello strato iniziale"
+msgstr "Velocità di spostamento del layer iniziale"
#: fdmprinter.def.json
msgctxt "speed_travel_layer_0 description"
msgid "The speed of travel moves in the initial layer. A lower value is advised to prevent pulling previously printed parts away from the build plate. The value of this setting can automatically be calculated from the ratio between the Travel Speed and the Print Speed."
-msgstr "Indica la velocità di spostamento per lo strato iniziale. Un valore inferiore è consigliabile per evitare di rimuovere le parti precedentemente stampate dal piano di stampa. Il valore di questa impostazione può essere calcolato automaticamente dal rapporto tra la velocità di spostamento e la velocità di stampa."
+msgstr "Indica la velocità di spostamento del layer iniziale. Un valore inferiore è consigliabile per evitare di rimuovere le parti precedentemente stampate dal piano di stampa. Il valore di questa impostazione può essere calcolato automaticamente dal rapporto tra la velocità di spostamento e la velocità di stampa."
#: fdmprinter.def.json
msgctxt "skirt_brim_speed label"
@@ -2127,12 +2123,12 @@ msgstr "Indica la velocità massima di spostamento del piano di stampa. L’impo
#: fdmprinter.def.json
msgctxt "speed_slowdown_layers label"
msgid "Number of Slower Layers"
-msgstr "Numero di strati stampati a velocità inferiore"
+msgstr "Numero di layers stampati a velocità inferiore"
#: fdmprinter.def.json
msgctxt "speed_slowdown_layers description"
msgid "The first few layers are printed slower than the rest of the model, to get better adhesion to the build plate and improve the overall success rate of prints. The speed is gradually increased over these layers."
-msgstr "I primi strati vengono stampati più lentamente rispetto al resto del modello, per ottenere una migliore adesione al piano di stampa ed ottimizzare nel complesso la percentuale di successo delle stampe. La velocità aumenta gradualmente nel corso di esecuzione degli strati successivi."
+msgstr "I primi layers vengono stampati più lentamente rispetto al resto del modello, per ottenere una migliore adesione al piano di stampa ed ottimizzare nel complesso la percentuale di successo delle stampe. La velocità aumenta gradualmente nel corso di esecuzione dei layers successivi."
#: fdmprinter.def.json
msgctxt "speed_equalize_flow_enabled label"
@@ -2287,12 +2283,12 @@ msgstr "Accelerazione alla quale vengono stampate le parti inferiori del support
#: fdmprinter.def.json
msgctxt "acceleration_prime_tower label"
msgid "Prime Tower Acceleration"
-msgstr "Accelerazione della torre di innesco"
+msgstr "Accelerazione della Prime Tower"
#: fdmprinter.def.json
msgctxt "acceleration_prime_tower description"
msgid "The acceleration with which the prime tower is printed."
-msgstr "Indica l’accelerazione con cui viene stampata la torre di innesco."
+msgstr "Indica l’accelerazione con cui viene stampata la Prime Tower."
#: fdmprinter.def.json
msgctxt "acceleration_travel label"
@@ -2307,32 +2303,32 @@ msgstr "Indica l’accelerazione alla quale vengono effettuati gli spostamenti."
#: fdmprinter.def.json
msgctxt "acceleration_layer_0 label"
msgid "Initial Layer Acceleration"
-msgstr "Accelerazione dello strato iniziale"
+msgstr "Accelerazione del layer iniziale"
#: fdmprinter.def.json
msgctxt "acceleration_layer_0 description"
msgid "The acceleration for the initial layer."
-msgstr "Indica l’accelerazione dello strato iniziale."
+msgstr "Indica l’accelerazione del layer iniziale."
#: fdmprinter.def.json
msgctxt "acceleration_print_layer_0 label"
msgid "Initial Layer Print Acceleration"
-msgstr "Accelerazione di stampa strato iniziale"
+msgstr "Accelerazione di stampa layer iniziale"
#: fdmprinter.def.json
msgctxt "acceleration_print_layer_0 description"
msgid "The acceleration during the printing of the initial layer."
-msgstr "Indica l’accelerazione durante la stampa dello strato iniziale."
+msgstr "Indica l’accelerazione durante la stampa del layer iniziale."
#: fdmprinter.def.json
msgctxt "acceleration_travel_layer_0 label"
msgid "Initial Layer Travel Acceleration"
-msgstr "Accelerazione spostamenti dello strato iniziale"
+msgstr "Accelerazione spostamenti del layer iniziale"
#: fdmprinter.def.json
msgctxt "acceleration_travel_layer_0 description"
msgid "The acceleration for travel moves in the initial layer."
-msgstr "Indica l’accelerazione degli spostamenti dello strato iniziale."
+msgstr "Indica l’accelerazione degli spostamenti del layer iniziale."
#: fdmprinter.def.json
msgctxt "acceleration_skirt_brim label"
@@ -2342,7 +2338,7 @@ msgstr "Accelerazione skirt/brim"
#: 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 "Indica l’accelerazione alla quale sono stampati lo skirt ed il brim. Normalmente questa operazione viene svolta all’accelerazione dello strato iniziale, ma a volte è possibile che si desideri stampare lo skirt o il brim ad un’accelerazione diversa."
+msgstr "Indica l’accelerazione alla quale sono stampati lo skirt ed il brim. Normalmente questa operazione viene svolta all’accelerazione del layer iniziale, ma a volte è possibile che si desideri stampare lo skirt o il brim ad un’accelerazione diversa."
#: fdmprinter.def.json
msgctxt "jerk_enabled label"
@@ -2352,7 +2348,7 @@ msgstr "Abilita controllo jerk"
#: fdmprinter.def.json
msgctxt "jerk_enabled description"
msgid "Enables adjusting the jerk of print head when the velocity in the X or Y axis changes. Increasing the jerk can reduce printing time at the cost of print quality."
-msgstr "Abilita la regolazione del jerk della testina di stampa quando la velocità nell’asse X o Y cambia. Aumentando il jerk il tempo di stampa si riduce a discapito della qualità di stampa."
+msgstr "Abilita la regolazione del jerk della testa di stampa quando la velocità nell’asse X o Y cambia. Aumentando il jerk il tempo di stampa si riduce a discapito della qualità di stampa."
#: fdmprinter.def.json
msgctxt "jerk_print label"
@@ -2362,7 +2358,7 @@ msgstr "Jerk stampa"
#: fdmprinter.def.json
msgctxt "jerk_print description"
msgid "The maximum instantaneous velocity change of the print head."
-msgstr "Indica il cambio della velocità istantanea massima della testina di stampa."
+msgstr "Indica il cambio della velocità istantanea massima della testa di stampa."
#: fdmprinter.def.json
msgctxt "jerk_infill label"
@@ -2412,7 +2408,7 @@ msgstr "Jerk del rivestimento superficie superiore"
#: fdmprinter.def.json
msgctxt "jerk_roofing description"
msgid "The maximum instantaneous velocity change with which top surface skin layers are printed."
-msgstr "Indica la variazione di velocità istantanea massima con cui vengono stampati gli strati rivestimento superficie superiore."
+msgstr "Indica la variazione di velocità istantanea massima con cui vengono stampati i layers rivestimento superficie superiore."
#: fdmprinter.def.json
msgctxt "jerk_topbottom label"
@@ -2422,7 +2418,7 @@ msgstr "Jerk strato superiore/inferiore"
#: fdmprinter.def.json
msgctxt "jerk_topbottom description"
msgid "The maximum instantaneous velocity change with which top/bottom layers are printed."
-msgstr "Indica il cambio della velocità istantanea massima con cui vengono stampati gli strati superiore/inferiore."
+msgstr "Indica il cambio della velocità istantanea massima con cui vengono stampati i layers superiore/inferiore."
#: fdmprinter.def.json
msgctxt "jerk_support label"
@@ -2477,12 +2473,12 @@ msgstr "Indica la variazione della velocità istantanea massima con cui vengono
#: fdmprinter.def.json
msgctxt "jerk_prime_tower label"
msgid "Prime Tower Jerk"
-msgstr "Jerk della torre di innesco"
+msgstr "Jerk della Prime Tower"
#: fdmprinter.def.json
msgctxt "jerk_prime_tower description"
msgid "The maximum instantaneous velocity change with which the prime tower is printed."
-msgstr "Indica il cambio della velocità istantanea massima con cui viene stampata la torre di innesco del supporto."
+msgstr "Indica il cambio della velocità istantanea massima con cui viene stampata la Prime Tower del supporto."
#: fdmprinter.def.json
msgctxt "jerk_travel label"
@@ -2497,32 +2493,32 @@ msgstr "Indica il cambio della velocità istantanea massima con cui vengono effe
#: fdmprinter.def.json
msgctxt "jerk_layer_0 label"
msgid "Initial Layer Jerk"
-msgstr "Jerk dello strato iniziale"
+msgstr "Jerk del layer iniziale"
#: fdmprinter.def.json
msgctxt "jerk_layer_0 description"
msgid "The print maximum instantaneous velocity change for the initial layer."
-msgstr "Indica il cambio della velocità istantanea massima dello strato iniziale."
+msgstr "Indica il cambio della velocità istantanea massima del layer iniziale."
#: fdmprinter.def.json
msgctxt "jerk_print_layer_0 label"
msgid "Initial Layer Print Jerk"
-msgstr "Jerk di stampa strato iniziale"
+msgstr "Jerk di stampa layer iniziale"
#: fdmprinter.def.json
msgctxt "jerk_print_layer_0 description"
msgid "The maximum instantaneous velocity change during the printing of the initial layer."
-msgstr "Indica il cambio della velocità istantanea massima durante la stampa dello strato iniziale."
+msgstr "Indica il cambio della velocità istantanea massima durante la stampa del layer iniziale."
#: fdmprinter.def.json
msgctxt "jerk_travel_layer_0 label"
msgid "Initial Layer Travel Jerk"
-msgstr "Jerk spostamenti dello strato iniziale"
+msgstr "Jerk spostamenti del layer iniziale"
#: fdmprinter.def.json
msgctxt "jerk_travel_layer_0 description"
msgid "The acceleration for travel moves in the initial layer."
-msgstr "Indica l’accelerazione degli spostamenti dello strato iniziale."
+msgstr "Indica l’accelerazione degli spostamenti del layer iniziale."
#: fdmprinter.def.json
msgctxt "jerk_skirt_brim label"
@@ -2602,22 +2598,22 @@ msgstr "La distanza tra l’ugello e le parti già stampate quando si effettua l
#: fdmprinter.def.json
msgctxt "start_layers_at_same_position label"
msgid "Start Layers with the Same Part"
-msgstr "Avvio strati con la stessa parte"
+msgstr "Avvio layers con la stessa parte"
#: fdmprinter.def.json
msgctxt "start_layers_at_same_position description"
msgid "In each layer start with printing the object near the same point, so that we don't start a new layer with printing the piece which the previous layer ended with. This makes for better overhangs and small parts, but increases printing time."
-msgstr "In ciascuno strato inizia la stampa dell’oggetto vicino allo stesso punto, in modo che non si inizia un nuovo strato con la stampa del pezzo con cui è terminato lo strato precedente. Questo consente di ottenere migliori sovrapposizioni e parti piccole, ma aumenta il tempo di stampa."
+msgstr "In ciascun layer inizia la stampa dell’oggetto vicino allo stesso punto, in modo che non si inizi un nuovo layer con la stampa del pezzo con cui è terminato il layer precedente. Questo consente di ottenere migliori sovrapposizioni e parti piccole, ma aumenta il tempo di stampa."
#: fdmprinter.def.json
msgctxt "layer_start_x label"
msgid "Layer Start X"
-msgstr "Avvio strato X"
+msgstr "Avvio layer X"
#: fdmprinter.def.json
msgctxt "layer_start_x description"
msgid "The X coordinate of the position near where to find the part to start printing each layer."
-msgstr "La coordinata X della posizione in prossimità della quale si trova la parte per avviare la stampa di ciascuno strato."
+msgstr "La coordinata X della posizione in prossimità della quale si trova la parte per avviare la stampa di ciascun layer."
#: fdmprinter.def.json
msgctxt "layer_start_y label"
@@ -2627,7 +2623,7 @@ msgstr "Avvio strato Y"
#: fdmprinter.def.json
msgctxt "layer_start_y description"
msgid "The Y coordinate of the position near where to find the part to start printing each layer."
-msgstr "La coordinata Y della posizione in prossimità della quale si trova la parte per avviare la stampa di ciascuno strato."
+msgstr "La coordinata Y della posizione in prossimità della quale si trova la parte per avviare la stampa di ciascun layer."
#: fdmprinter.def.json
msgctxt "retraction_hop_enabled label"
@@ -2687,7 +2683,7 @@ msgstr "Abilitazione raffreddamento stampa"
#: fdmprinter.def.json
msgctxt "cool_fan_enabled description"
msgid "Enables the print cooling fans while printing. The fans improve print quality on layers with short layer times and bridging / overhangs."
-msgstr "Abilita le ventole di raffreddamento durante la stampa. Le ventole migliorano la qualità di stampa sugli strati con tempi per strato più brevi e ponti/sbalzi."
+msgstr "Abilita le ventole di raffreddamento durante la stampa. Le ventole migliorano la qualità di stampa sui layers con tempi per layer più brevi e ponti/sbalzi."
#: fdmprinter.def.json
msgctxt "cool_fan_speed label"
@@ -2707,7 +2703,7 @@ msgstr "Velocità regolare della ventola"
#: fdmprinter.def.json
msgctxt "cool_fan_speed_min description"
msgid "The speed at which the fans spin before hitting the threshold. When a layer prints faster than the threshold, the fan speed gradually inclines towards the maximum fan speed."
-msgstr "Indica la velocità alla quale ruotano le ventole prima di raggiungere la soglia. Quando uno strato viene stampato a una velocità superiore alla soglia, la velocità della ventola tende gradualmente verso la velocità massima della ventola."
+msgstr "Indica la velocità alla quale ruotano le ventole prima di raggiungere la soglia. Quando un layer viene stampato a una velocità superiore alla soglia, la velocità della ventola tende gradualmente verso la velocità massima della ventola."
#: fdmprinter.def.json
msgctxt "cool_fan_speed_max label"
@@ -2717,7 +2713,7 @@ msgstr "Velocità massima della ventola"
#: fdmprinter.def.json
msgctxt "cool_fan_speed_max description"
msgid "The speed at which the fans spin on the minimum layer time. The fan speed gradually increases between the regular fan speed and maximum fan speed when the threshold is hit."
-msgstr "Indica la velocità di rotazione della ventola al tempo minimo per strato. La velocità della ventola aumenta gradualmente tra la velocità regolare della ventola e la velocità massima della ventola quando viene raggiunta la soglia."
+msgstr "Indica la velocità di rotazione della ventola al tempo minimo per layer. La velocità della ventola aumenta gradualmente tra la velocità regolare della ventola e la velocità massima della ventola quando viene raggiunta la soglia."
#: fdmprinter.def.json
msgctxt "cool_min_layer_time_fan_speed_max label"
@@ -2727,7 +2723,7 @@ msgstr "Soglia velocità regolare/massima della ventola"
#: fdmprinter.def.json
msgctxt "cool_min_layer_time_fan_speed_max description"
msgid "The layer time which sets the threshold between regular fan speed and maximum fan speed. Layers that print slower than this time use regular fan speed. For faster layers the fan speed gradually increases towards the maximum fan speed."
-msgstr "Indica il tempo per strato che definisce la soglia tra la velocità regolare e quella massima della ventola. Gli strati che vengono stampati a una velocità inferiore a questo valore utilizzano una velocità regolare della ventola. Per gli strati stampati più velocemente la velocità della ventola aumenta gradualmente verso la velocità massima della ventola."
+msgstr "Indica il tempo per layer che definisce la soglia tra la velocità regolare e quella massima della ventola. Gli strati che vengono stampati a una velocità inferiore a questo valore utilizzano una velocità regolare della ventola. Per i layers stampati più velocemente la velocità della ventola aumenta gradualmente verso la velocità massima della ventola."
#: fdmprinter.def.json
msgctxt "cool_fan_speed_0 label"
@@ -2737,7 +2733,7 @@ msgstr "Velocità iniziale della ventola"
#: fdmprinter.def.json
msgctxt "cool_fan_speed_0 description"
msgid "The speed at which the fans spin at the start of the print. In subsequent layers the fan speed is gradually increased up to the layer corresponding to Regular Fan Speed at Height."
-msgstr "La velocità di rotazione della ventola all’inizio della stampa. Negli strati successivi la velocità della ventola aumenta gradualmente da zero fino allo strato corrispondente alla velocità regolare in altezza."
+msgstr "La velocità di rotazione della ventola all’inizio della stampa. Nei layers successivi la velocità della ventola aumenta gradualmente da zero fino allo strato corrispondente alla velocità regolare in altezza."
#: fdmprinter.def.json
msgctxt "cool_fan_full_at_height label"
@@ -2747,27 +2743,27 @@ msgstr "Velocità regolare della ventola in altezza"
#: fdmprinter.def.json
msgctxt "cool_fan_full_at_height description"
msgid "The height at which the fans spin on regular fan speed. At the layers below the fan speed gradually increases from Initial Fan Speed to Regular Fan Speed."
-msgstr "Indica l’altezza alla quale la ventola ruota alla velocità regolare. Agli strati stampati a velocità inferiore la velocità della ventola aumenta gradualmente dalla velocità iniziale a quella regolare."
+msgstr "Indica l’altezza alla quale la ventola ruota alla velocità regolare. ai layers stampati a velocità inferiore la velocità della ventola aumenta gradualmente dalla velocità iniziale a quella regolare."
#: fdmprinter.def.json
msgctxt "cool_fan_full_layer label"
msgid "Regular Fan Speed at Layer"
-msgstr "Velocità regolare della ventola in corrispondenza dello strato"
+msgstr "Velocità regolare della ventola in corrispondenza del layer"
#: fdmprinter.def.json
msgctxt "cool_fan_full_layer description"
msgid "The layer at which the fans spin on regular fan speed. If regular fan speed at height is set, this value is calculated and rounded to a whole number."
-msgstr "Indica lo strato in corrispondenza del quale la ventola ruota alla velocità regolare. Se è impostata la velocità regolare in altezza, questo valore viene calcolato e arrotondato a un numero intero."
+msgstr "Indica il layer in corrispondenza del quale la ventola ruota alla velocità regolare. Se è impostata la velocità regolare in altezza, questo valore viene calcolato e arrotondato a un numero intero."
#: fdmprinter.def.json
msgctxt "cool_min_layer_time label"
msgid "Minimum Layer Time"
-msgstr "Tempo minimo per strato"
+msgstr "Tempo minimo per layer"
#: fdmprinter.def.json
msgctxt "cool_min_layer_time description"
msgid "The minimum time spent in a layer. This forces the printer to slow down, to at least spend the time set here in one layer. This allows the printed material to cool down properly before printing the next layer. Layers may still take shorter than the minimal layer time if Lift Head is disabled and if the Minimum Speed would otherwise be violated."
-msgstr "Indica il tempo minimo dedicato a uno strato. Questo forza la stampante a rallentare, per impiegare almeno il tempo impostato qui per uno strato. Questo consente il corretto raffreddamento del materiale stampato prima di procedere alla stampa dello strato successivo. La stampa degli strati potrebbe richiedere un tempo inferiore al minimo se la funzione Sollevamento della testina è disabilitata e se la velocità minima non viene rispettata."
+msgstr "Indica il tempo minimo dedicato a un layer. Questo forza la stampante a rallentare, per impiegare almeno il tempo impostato qui per un layer. Questo consente il corretto raffreddamento del materiale stampato prima di procedere alla stampa del layer successivo. La stampa dei layers potrebbe richiedere un tempo inferiore al minimo se la funzione Sollevamento della testina è disabilitata e se la velocità minima non viene rispettata."
#: fdmprinter.def.json
msgctxt "cool_min_speed label"
@@ -2777,7 +2773,7 @@ msgstr "Velocità minima"
#: fdmprinter.def.json
msgctxt "cool_min_speed description"
msgid "The minimum print speed, despite slowing down due to the minimum layer time. When the printer would slow down too much, the pressure in the nozzle would be too low and result in bad print quality."
-msgstr "Indica la velocità minima di stampa, a prescindere dal rallentamento per il tempo minimo per strato. Quando la stampante rallenta eccessivamente, la pressione nell’ugello risulta insufficiente con conseguente scarsa qualità di stampa."
+msgstr "Indica la velocità minima di stampa, a prescindere dal rallentamento per il tempo minimo per layer. Quando la stampante rallenta eccessivamente, la pressione nell’ugello risulta insufficiente con conseguente scarsa qualità di stampa."
#: fdmprinter.def.json
msgctxt "cool_lift_head label"
@@ -2787,7 +2783,7 @@ msgstr "Sollevamento della testina"
#: fdmprinter.def.json
msgctxt "cool_lift_head description"
msgid "When the minimum speed is hit because of minimum layer time, lift the head away from the print and wait the extra time until the minimum layer time is reached."
-msgstr "Quando viene raggiunta la velocità minima per il tempo minimo per strato, sollevare la testina dalla stampa e attendere il tempo supplementare fino al raggiungimento del valore per tempo minimo per strato."
+msgstr "Quando viene raggiunta la velocità minima per il tempo minimo per layer, sollevare la testina dalla stampa e attendere il tempo supplementare fino al raggiungimento del valore per tempo minimo per layer."
#: fdmprinter.def.json
msgctxt "support label"
@@ -2817,7 +2813,7 @@ msgstr "Estrusore del supporto"
#: fdmprinter.def.json
msgctxt "support_extruder_nr description"
msgid "The extruder train to use for printing the support. This is used in multi-extrusion."
-msgstr "Il treno estrusore utilizzato per la stampa del supporto. Utilizzato nell’estrusione multipla."
+msgstr "Il blocco estrusore utilizzato per la stampa del supporto. Utilizzato nell’estrusione multipla."
#: fdmprinter.def.json
msgctxt "support_infill_extruder_nr label"
@@ -2827,17 +2823,17 @@ msgstr "Estrusore riempimento del supporto"
#: fdmprinter.def.json
msgctxt "support_infill_extruder_nr description"
msgid "The extruder train to use for printing the infill of the support. This is used in multi-extrusion."
-msgstr "Il treno estrusore utilizzato per la stampa del riempimento del supporto. Utilizzato nell’estrusione multipla."
+msgstr "Il blocco estrusore utilizzato per la stampa del riempimento del supporto. Utilizzato nell’estrusione multipla."
#: fdmprinter.def.json
msgctxt "support_extruder_nr_layer_0 label"
msgid "First Layer Support Extruder"
-msgstr "Estrusore del supporto primo strato"
+msgstr "Estrusore del supporto primo layer"
#: fdmprinter.def.json
msgctxt "support_extruder_nr_layer_0 description"
msgid "The extruder train to use for printing the first layer of support infill. This is used in multi-extrusion."
-msgstr "Il treno estrusore utilizzato per la stampa del primo strato del riempimento del supporto. Utilizzato nell’estrusione multipla."
+msgstr "Il blocco estrusore utilizzato per la stampa del primo layer del riempimento del supporto. Utilizzato nell’estrusione multipla."
#: fdmprinter.def.json
msgctxt "support_interface_extruder_nr label"
@@ -2847,7 +2843,7 @@ msgstr "Estrusore interfaccia del supporto"
#: fdmprinter.def.json
msgctxt "support_interface_extruder_nr description"
msgid "The extruder train to use for printing the roofs and floors of the support. This is used in multi-extrusion."
-msgstr "Treno estrusore utilizzato per la stampa delle parti superiori e inferiori del supporto. Utilizzato nell’estrusione multipla."
+msgstr "Blocco estrusore utilizzato per la stampa delle parti superiori e inferiori del supporto. Utilizzato nell’estrusione multipla."
#: fdmprinter.def.json
msgctxt "support_roof_extruder_nr label"
@@ -2857,7 +2853,7 @@ msgstr "Estrusore parte superiore del supporto"
#: fdmprinter.def.json
msgctxt "support_roof_extruder_nr description"
msgid "The extruder train to use for printing the roofs of the support. This is used in multi-extrusion."
-msgstr "Treno estrusore utilizzato per la stampa delle parti superiori del supporto. Utilizzato nell’estrusione multipla."
+msgstr "Blocco estrusore utilizzato per la stampa delle parti superiori del supporto. Utilizzato nell’estrusione multipla."
#: fdmprinter.def.json
msgctxt "support_bottom_extruder_nr label"
@@ -2867,7 +2863,7 @@ msgstr "Estrusore parte inferiore del supporto"
#: fdmprinter.def.json
msgctxt "support_bottom_extruder_nr description"
msgid "The extruder train to use for printing the floors of the support. This is used in multi-extrusion."
-msgstr "Treno estrusore utilizzato per la stampa delle parti inferiori del supporto. Utilizzato nell’estrusione multipla."
+msgstr "Blocco estrusore utilizzato per la stampa delle parti inferiori del supporto. Utilizzato nell’estrusione multipla."
#: fdmprinter.def.json
msgctxt "support_type label"
@@ -3087,12 +3083,12 @@ msgstr "È l'entità di offset (estensione dello strato) applicato a tutti i pol
#: fdmprinter.def.json
msgctxt "support_infill_sparse_thickness label"
msgid "Support Infill Layer Thickness"
-msgstr "Spessore dello strato di riempimento di supporto"
+msgstr "Spessore dello layer di riempimento di supporto"
#: fdmprinter.def.json
msgctxt "support_infill_sparse_thickness description"
msgid "The thickness per layer of support infill material. This value should always be a multiple of the layer height and is otherwise rounded."
-msgstr "Indica lo spessore per strato del materiale di riempimento del supporto. Questo valore deve sempre essere un multiplo dell’altezza dello strato e in caso contrario viene arrotondato."
+msgstr "Indica lo spessore per layer del materiale di riempimento del supporto. Questo valore deve sempre essere un multiplo dell’altezza del layer e in caso contrario viene arrotondato."
#: fdmprinter.def.json
msgctxt "gradual_support_infill_steps label"
@@ -3172,7 +3168,7 @@ msgstr "Spessore parte inferiore del supporto"
#: fdmprinter.def.json
msgctxt "support_bottom_height description"
msgid "The thickness of the support floors. This controls the number of dense layers that are printed on top of places of a model on which support rests."
-msgstr "Indica lo spessore delle parti inferiori del supporto. Questo controlla il numero di strati fitti stampati sulla sommità dei punti di un modello su cui appoggia un supporto."
+msgstr "Indica lo spessore delle parti inferiori del supporto. Questo controlla il numero di layers fitti stampati sulla sommità dei punti di un modello su cui appoggia un supporto."
#: fdmprinter.def.json
msgctxt "support_interface_skip_height label"
@@ -3402,7 +3398,7 @@ msgstr "Maglia supporto di discesa"
#: fdmprinter.def.json
msgctxt "support_mesh_drop_down description"
msgid "Make support everywhere below the support mesh, so that there's no overhang in the support mesh."
-msgstr "Rappresenta il supporto ovunque sotto la maglia di supporto, in modo che in questa non vi siano punti a sbalzo."
+msgstr "Genera supporti ovunque sotto la maglia di supporto, in modo che in questa non vi siano punti a sbalzo."
#: fdmprinter.def.json
msgctxt "platform_adhesion label"
@@ -3482,7 +3478,7 @@ msgstr "Estrusore adesione piano di stampa"
#: 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 "Il treno estrusore utilizzato per la stampa dello skirt/brim/raft. Utilizzato nell’estrusione multipla."
+msgstr "Il blocco estrusore utilizzato per la stampa dello skirt/brim/raft. Utilizzato nell’estrusione multipla."
#: fdmprinter.def.json
msgctxt "skirt_line_count label"
@@ -3504,9 +3500,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"
@@ -3576,37 +3570,37 @@ msgstr "Traferro del raft"
#: fdmprinter.def.json
msgctxt "raft_airgap description"
msgid "The gap between the final raft layer and the first layer of the model. Only the first layer is raised by this amount to lower the bonding between the raft layer and the model. Makes it easier to peel off the raft."
-msgstr "È l'interstizio tra lo strato di raft finale ed il primo strato del modello. Solo il primo strato viene sollevato di questo valore per ridurre l'adesione fra lo strato di raft e il modello. Ciò rende più facile rimuovere il raft."
+msgstr "È l'interstizio tra il layer del raft finale ed il primo layer del modello. Solo il primo layer viene sollevato di questo valore per ridurre l'adesione fra lo strato di raft e il modello. Ciò rende più facile rimuovere il raft."
#: fdmprinter.def.json
msgctxt "layer_0_z_overlap label"
msgid "Initial Layer Z Overlap"
-msgstr "Z Sovrapposizione Primo Strato"
+msgstr "Z Sovrapposizione Primo Layer"
#: fdmprinter.def.json
msgctxt "layer_0_z_overlap description"
msgid "Make the first and second layer of the model overlap in the Z direction to compensate for the filament lost in the airgap. All models above the first model layer will be shifted down by this amount."
-msgstr "Effettua il primo e secondo strato di sovrapposizione modello nella direzione Z per compensare il filamento perso nel traferro. Tutti i modelli sopra il primo strato del modello saranno spostati verso il basso di questa quantità."
+msgstr "Effettua il primo e secondo layer di sovrapposizione modello nella direzione Z per compensare il filamento perso nel vuoto. Tutti i modelli sopra il primo layer del modello saranno spostati verso il basso di questa quantità."
#: fdmprinter.def.json
msgctxt "raft_surface_layers label"
msgid "Raft Top Layers"
-msgstr "Strati superiori del raft"
+msgstr "Layers superiori del raft"
#: fdmprinter.def.json
msgctxt "raft_surface_layers description"
msgid "The number of top layers on top of the 2nd raft layer. These are fully filled layers that the model sits on. 2 layers result in a smoother top surface than 1."
-msgstr "Numero di strati sulla parte superiore del secondo strato del raft. Si tratta di strati completamente riempiti su cui poggia il modello. 2 strati danno come risultato una superficie superiore più levigata rispetto ad 1 solo strato."
+msgstr "Numero di layers sulla parte superiore del secondo layer del raft. Si tratta di layers completamente riempiti su cui poggia il modello. 2 layers danno come risultato una superficie superiore più levigata rispetto ad 1 solo layer."
#: fdmprinter.def.json
msgctxt "raft_surface_thickness label"
msgid "Raft Top Layer Thickness"
-msgstr "Spessore dello strato superiore del raft"
+msgstr "Spessore del layer superiore del raft"
#: fdmprinter.def.json
msgctxt "raft_surface_thickness description"
msgid "Layer thickness of the top raft layers."
-msgstr "È lo spessore degli strati superiori del raft."
+msgstr "È lo spessore dei layers superiori del raft."
#: fdmprinter.def.json
msgctxt "raft_surface_line_width label"
@@ -3631,17 +3625,17 @@ msgstr "Indica la distanza tra le linee che costituiscono la maglia superiore de
#: fdmprinter.def.json
msgctxt "raft_interface_thickness label"
msgid "Raft Middle Thickness"
-msgstr "Spessore dello strato intermedio del raft"
+msgstr "Spessore del layer intermedio del raft"
#: fdmprinter.def.json
msgctxt "raft_interface_thickness description"
msgid "Layer thickness of the middle raft layer."
-msgstr "È lo spessore dello strato intermedio del raft."
+msgstr "È lo spessore del layer intermedio del raft."
#: fdmprinter.def.json
msgctxt "raft_interface_line_width label"
msgid "Raft Middle Line Width"
-msgstr "Larghezza delle linee dello strato intermedio del raft"
+msgstr "Larghezza delle linee del layer intermedio del raft"
#: fdmprinter.def.json
msgctxt "raft_interface_line_width description"
@@ -3651,12 +3645,12 @@ msgstr "Indica la larghezza delle linee dello strato intermedio del raft. Una ma
#: fdmprinter.def.json
msgctxt "raft_interface_line_spacing label"
msgid "Raft Middle Spacing"
-msgstr "Spaziatura dello strato intermedio del raft"
+msgstr "Spaziatura del layer intermedio del raft"
#: fdmprinter.def.json
msgctxt "raft_interface_line_spacing description"
msgid "The distance between the raft lines for the middle raft layer. The spacing of the middle should be quite wide, while being dense enough to support the top raft layers."
-msgstr "Indica la distanza fra le linee dello strato intermedio del raft. La spaziatura dello strato intermedio deve essere abbastanza ampia, ma al tempo stesso sufficientemente fitta da sostenere gli strati superiori del raft."
+msgstr "Indica la distanza fra le linee del layer intermedio del raft. La spaziatura del layer intermedio deve essere abbastanza ampia, ma al tempo stesso sufficientemente fitta da sostenere i layers superiori del raft."
#: fdmprinter.def.json
msgctxt "raft_base_thickness label"
@@ -3666,17 +3660,17 @@ msgstr "Spessore della base del raft"
#: fdmprinter.def.json
msgctxt "raft_base_thickness description"
msgid "Layer thickness of the base raft layer. This should be a thick layer which sticks firmly to the printer build plate."
-msgstr "Indica lo spessore dello strato di base del raft. Questo strato deve essere spesso per aderire saldamente al piano di stampa."
+msgstr "Indica lo spessore del layer di base del raft. Questo layer deve essere spesso per aderire saldamente al piano di stampa."
#: fdmprinter.def.json
msgctxt "raft_base_line_width label"
msgid "Raft Base Line Width"
-msgstr "Larghezza delle linee dello strato di base del raft"
+msgstr "Larghezza delle linee del layer di base del raft"
#: fdmprinter.def.json
msgctxt "raft_base_line_width description"
msgid "Width of the lines in the base raft layer. These should be thick lines to assist in build plate adhesion."
-msgstr "Indica la larghezza delle linee dello strato di base del raft. Le linee di questo strato devono essere spesse per favorire l'adesione al piano di stampa."
+msgstr "Indica la larghezza delle linee del layer di base del raft. Le linee di questo layer devono essere spesse per favorire l'adesione al piano di stampa."
#: fdmprinter.def.json
msgctxt "raft_base_line_spacing label"
@@ -3686,7 +3680,7 @@ msgstr "Spaziatura delle linee del raft"
#: fdmprinter.def.json
msgctxt "raft_base_line_spacing description"
msgid "The distance between the raft lines for the base raft layer. Wide spacing makes for easy removal of the raft from the build plate."
-msgstr "Indica la distanza tra le linee che costituiscono lo strato di base del raft. Un'ampia spaziatura favorisce la rimozione del raft dal piano di stampa."
+msgstr "Indica la distanza tra le linee che costituiscono il layer di base del raft. Un'ampia spaziatura favorisce la rimozione del raft dal piano di stampa."
#: fdmprinter.def.json
msgctxt "raft_speed label"
@@ -3706,7 +3700,7 @@ msgstr "Velocità di stampa parte superiore del raft"
#: fdmprinter.def.json
msgctxt "raft_surface_speed description"
msgid "The speed at which the top raft layers are printed. These should be printed a bit slower, so that the nozzle can slowly smooth out adjacent surface lines."
-msgstr "Indica la velocità alla quale sono stampati gli strati superiori del raft. La stampa di questi strati deve avvenire un po' più lentamente, in modo da consentire all'ugello di levigare lentamente le linee superficiali adiacenti."
+msgstr "Indica la velocità alla quale sono stampati i layers superiori del raft. La stampa di questi layers deve avvenire un po' più lentamente, in modo da consentire all'ugello di levigare lentamente le linee superficiali adiacenti."
#: fdmprinter.def.json
msgctxt "raft_interface_speed label"
@@ -3716,7 +3710,7 @@ msgstr "Velocità di stampa raft intermedio"
#: fdmprinter.def.json
msgctxt "raft_interface_speed description"
msgid "The speed at which the middle raft layer is printed. This should be printed quite slowly, as the volume of material coming out of the nozzle is quite high."
-msgstr "Indica la velocità alla quale viene stampato lo strato intermedio del raft. La sua stampa deve avvenire molto lentamente, considerato che il volume di materiale che fuoriesce dall'ugello è piuttosto elevato."
+msgstr "Indica la velocità alla quale viene stampato il layer intermedio del raft. La sua stampa deve avvenire molto lentamente, considerato che il volume di materiale che fuoriesce dall'ugello è piuttosto elevato."
#: fdmprinter.def.json
msgctxt "raft_base_speed label"
@@ -3746,7 +3740,7 @@ msgstr "Accelerazione di stampa parte superiore del raft"
#: fdmprinter.def.json
msgctxt "raft_surface_acceleration description"
msgid "The acceleration with which the top raft layers are printed."
-msgstr "Indica l’accelerazione alla quale vengono stampati gli strati superiori del raft."
+msgstr "Indica l’accelerazione alla quale vengono stampati i layers superiori del raft."
#: fdmprinter.def.json
msgctxt "raft_interface_acceleration label"
@@ -3756,7 +3750,7 @@ msgstr "Accelerazione di stampa raft intermedio"
#: fdmprinter.def.json
msgctxt "raft_interface_acceleration description"
msgid "The acceleration with which the middle raft layer is printed."
-msgstr "Indica l’accelerazione con cui viene stampato lo strato intermedio del raft."
+msgstr "Indica l’accelerazione con cui viene stampato il layer intermedio del raft."
#: fdmprinter.def.json
msgctxt "raft_base_acceleration label"
@@ -3766,7 +3760,7 @@ msgstr "Accelerazione di stampa della base del raft"
#: fdmprinter.def.json
msgctxt "raft_base_acceleration description"
msgid "The acceleration with which the base raft layer is printed."
-msgstr "Indica l’accelerazione con cui viene stampato lo strato di base del raft."
+msgstr "Indica l’accelerazione con cui viene stampato il layer di base del raft."
#: fdmprinter.def.json
msgctxt "raft_jerk label"
@@ -3786,7 +3780,7 @@ msgstr "Jerk di stampa parte superiore del raft"
#: fdmprinter.def.json
msgctxt "raft_surface_jerk description"
msgid "The jerk with which the top raft layers are printed."
-msgstr "Indica il jerk al quale vengono stampati gli strati superiori del raft."
+msgstr "Indica il jerk al quale vengono stampati i layers superiori del raft."
#: fdmprinter.def.json
msgctxt "raft_interface_jerk label"
@@ -3796,7 +3790,7 @@ msgstr "Jerk di stampa raft intermedio"
#: fdmprinter.def.json
msgctxt "raft_interface_jerk description"
msgid "The jerk with which the middle raft layer is printed."
-msgstr "Indica il jerk con cui viene stampato lo strato intermedio del raft."
+msgstr "Indica il jerk con cui viene stampato il layer intermedio del raft."
#: fdmprinter.def.json
msgctxt "raft_base_jerk label"
@@ -3806,7 +3800,7 @@ msgstr "Jerk di stampa della base del raft"
#: fdmprinter.def.json
msgctxt "raft_base_jerk description"
msgid "The jerk with which the base raft layer is printed."
-msgstr "Indica il jerk con cui viene stampato lo strato di base del raft."
+msgstr "Indica il jerk con cui viene stampato il layer di base del raft."
#: fdmprinter.def.json
msgctxt "raft_fan_speed label"
@@ -3826,7 +3820,7 @@ msgstr "Velocità della ventola per la parte superiore del raft"
#: fdmprinter.def.json
msgctxt "raft_surface_fan_speed description"
msgid "The fan speed for the top raft layers."
-msgstr "Indica la velocità di rotazione della ventola per gli strati superiori del raft."
+msgstr "Indica la velocità di rotazione della ventola per i layers superiori del raft."
#: fdmprinter.def.json
msgctxt "raft_interface_fan_speed label"
@@ -3836,7 +3830,7 @@ msgstr "Velocità della ventola per il raft intermedio"
#: fdmprinter.def.json
msgctxt "raft_interface_fan_speed description"
msgid "The fan speed for the middle raft layer."
-msgstr "Indica la velocità di rotazione della ventola per gli strati intermedi del raft."
+msgstr "Indica la velocità di rotazione della ventola per i layers intermedi del raft."
#: fdmprinter.def.json
msgctxt "raft_base_fan_speed label"
@@ -3846,7 +3840,7 @@ msgstr "Velocità della ventola per la base del raft"
#: fdmprinter.def.json
msgctxt "raft_base_fan_speed description"
msgid "The fan speed for the base raft layer."
-msgstr "Indica la velocità di rotazione della ventola per lo strato di base del raft."
+msgstr "Indica la velocità di rotazione della ventola per il layer di base del raft."
#: fdmprinter.def.json
msgctxt "dual label"
@@ -3861,7 +3855,7 @@ msgstr "Indica le impostazioni utilizzate per la stampa con estrusori multipli."
#: fdmprinter.def.json
msgctxt "prime_tower_enable label"
msgid "Enable Prime Tower"
-msgstr "Abilitazione torre di innesco"
+msgstr "Abilitazione Prime Tower"
#: fdmprinter.def.json
msgctxt "prime_tower_enable description"
@@ -3871,57 +3865,57 @@ msgstr "Stampa una torre accanto alla stampa che serve per innescare il material
#: fdmprinter.def.json
msgctxt "prime_tower_size label"
msgid "Prime Tower Size"
-msgstr "Dimensioni torre di innesco"
+msgstr "Dimensioni Prime Tower"
#: fdmprinter.def.json
msgctxt "prime_tower_size description"
msgid "The width of the prime tower."
-msgstr "Indica la larghezza della torre di innesco."
+msgstr "Indica la larghezza della Prime Tower."
#: fdmprinter.def.json
msgctxt "prime_tower_min_volume label"
msgid "Prime Tower Minimum Volume"
-msgstr "Volume minimo torre di innesco"
+msgstr "Volume minimo Prime Tower"
#: fdmprinter.def.json
msgctxt "prime_tower_min_volume description"
msgid "The minimum volume for each layer of the prime tower in order to purge enough material."
-msgstr "Il volume minimo per ciascuno strato della torre di innesco per scaricare materiale a sufficienza."
+msgstr "Il volume minimo per ciascun layer della Prime Tower per scaricare materiale a sufficienza."
#: fdmprinter.def.json
msgctxt "prime_tower_wall_thickness label"
msgid "Prime Tower Thickness"
-msgstr "Spessore torre di innesco"
+msgstr "Spessore Prime Tower"
#: fdmprinter.def.json
msgctxt "prime_tower_wall_thickness description"
msgid "The thickness of the hollow prime tower. A thickness larger than half the Prime Tower Minimum Volume will result in a dense prime tower."
-msgstr "Lo spessore della torre di innesco cava. Uno spessore superiore alla metà del volume minimo della torre di innesco genera una torre di innesco densa."
+msgstr "Lo spessore della Prime Tower cava. Uno spessore superiore alla metà del volume minimo della Prime Tower genera una torre di innesco densa."
#: fdmprinter.def.json
msgctxt "prime_tower_position_x label"
msgid "Prime Tower X Position"
-msgstr "Posizione X torre di innesco"
+msgstr "Posizione X Prime Tower"
#: fdmprinter.def.json
msgctxt "prime_tower_position_x description"
msgid "The x coordinate of the position of the prime tower."
-msgstr "Indica la coordinata X della posizione della torre di innesco."
+msgstr "Indica la coordinata X della posizione della Prime Tower."
#: fdmprinter.def.json
msgctxt "prime_tower_position_y label"
msgid "Prime Tower Y Position"
-msgstr "Posizione Y torre di innesco"
+msgstr "Posizione Y Prime Tower"
#: fdmprinter.def.json
msgctxt "prime_tower_position_y description"
msgid "The y coordinate of the position of the prime tower."
-msgstr "Indica la coordinata Y della posizione della torre di innesco."
+msgstr "Indica la coordinata Y della posizione della Prime Tower."
#: fdmprinter.def.json
msgctxt "prime_tower_flow label"
msgid "Prime Tower Flow"
-msgstr "Flusso torre di innesco"
+msgstr "Flusso Prime Tower"
#: fdmprinter.def.json
msgctxt "prime_tower_flow description"
@@ -3931,17 +3925,17 @@ msgstr "Determina la compensazione del flusso: la quantità di materiale estruso
#: fdmprinter.def.json
msgctxt "prime_tower_wipe_enabled label"
msgid "Wipe Inactive Nozzle on Prime Tower"
-msgstr "Ugello pulitura inattiva sulla torre di innesco"
+msgstr "Ugello pulitura inattiva sulla Prime Tower"
#: fdmprinter.def.json
msgctxt "prime_tower_wipe_enabled description"
msgid "After printing the prime tower with one nozzle, wipe the oozed material from the other nozzle off on the prime tower."
-msgstr "Dopo la stampa della torre di innesco con un ugello, pulisce il materiale fuoriuscito dall’altro ugello sulla torre di innesco."
+msgstr "Dopo la stampa della Prime Tower con un ugello, pulisce il materiale fuoriuscito dall’altro ugello sulla Prime Tower."
#: fdmprinter.def.json
msgctxt "dual_pre_wipe label"
msgid "Wipe Nozzle After Switch"
-msgstr "Ugello pulitura dopo commutazione"
+msgstr "Pulitura ugello dopo commutazione"
#: fdmprinter.def.json
msgctxt "dual_pre_wipe description"
@@ -3951,12 +3945,12 @@ msgstr "Dopo la commutazione dell’estrusore, pulire il materiale fuoriuscito d
#: fdmprinter.def.json
msgctxt "prime_tower_purge_volume label"
msgid "Prime Tower Purge Volume"
-msgstr "Volume di scarico torre di innesco"
+msgstr "Volume di scarico Prime Tower"
#: fdmprinter.def.json
msgctxt "prime_tower_purge_volume description"
msgid "Amount of filament to be purged when wiping on the prime tower. Purging is useful for compensating the filament lost by oozing during inactivity of the nozzle."
-msgstr "Quantità di filamento da scaricare durante la pulizia della torre di innesco. Lo scarico è utile per compensare il filamento perso per colatura durante l'inattività dell'ugello."
+msgstr "Quantità di filamento da scaricare durante la pulizia della Prime Tower. Lo scarico è utile per compensare il filamento perso per colatura durante l'inattività dell'ugello."
#: fdmprinter.def.json
msgctxt "ooze_shield_enabled label"
@@ -4096,7 +4090,7 @@ msgstr "Sequenza di stampa"
#: fdmprinter.def.json
msgctxt "print_sequence description"
msgid "Whether to print all models one layer at a time or to wait for one model to finish, before moving on to the next. One at a time mode is only possible if all models are separated in such a way that the whole print head can move in between and all models are lower than the distance between the nozzle and the X/Y axes."
-msgstr "Indica se stampare tutti i modelli uno strato alla volta o se attendere di terminare un modello prima di passare al successivo. La modalità 'uno per volta' è possibile solo se tutti i modelli sono separati in modo tale che l'intera testina di stampa possa muoversi tra di essi e se tutti i modelli sono più bassi della distanza tra l'ugello e gli assi X/Y."
+msgstr "Indica se stampare tutti i modelli uno layer alla volta o se attendere di terminare un modello prima di passare al successivo. La modalità 'uno per volta' è possibile solo se tutti i modelli sono separati in modo tale che l'intera testa di stampa possa muoversi tra di essi e se tutti i modelli sono più bassi della distanza tra l'ugello e gli assi X/Y."
#: fdmprinter.def.json
msgctxt "print_sequence option all_at_once"
@@ -4251,7 +4245,7 @@ msgstr "Estrusione relativa"
#: fdmprinter.def.json
msgctxt "relative_extrusion description"
msgid "Use relative extrusion rather than absolute extrusion. Using relative E-steps makes for easier post-processing of the Gcode. However, it's not supported by all printers and it may produce very slight deviations in the amount of deposited material compared to absolute E-steps. Irrespective of this setting, the extrusion mode will always be set to absolute before any Gcode script is output."
-msgstr "Utilizza l'estrusione relativa invece di quella assoluta. L'utilizzo di fasi E relative facilita la post-elaborazione del codice G. Tuttavia, questa impostazione non è supportata da tutte le stampanti e può causare deviazioni molto piccole nella quantità di materiale depositato rispetto alle fasi E assolute. Indipendentemente da questa impostazione, la modalità estrusione sarà sempre impostata su assoluta prima che venga generato uno script in codice G."
+msgstr "Utilizza l'estrusione relativa invece di quella assoluta. L'utilizzo di fasi E relative facilita la post-elaborazione del Gcode. Tuttavia, questa impostazione non è supportata da tutte le stampanti e può causare deviazioni molto piccole nella quantità di materiale depositato rispetto agli E-steps assoluti. Indipendentemente da questa impostazione, la modalità estrusione sarà sempre impostata su assoluta prima che venga generato uno script Gcode."
#: fdmprinter.def.json
msgctxt "experimental label"
@@ -4266,82 +4260,82 @@ msgstr "sperimentale!"
#: fdmprinter.def.json
msgctxt "support_tree_enable label"
msgid "Tree Support"
-msgstr ""
+msgstr "Supporto ad albero"
#: fdmprinter.def.json
msgctxt "support_tree_enable description"
msgid "Generate a tree-like support with branches that support your print. This may reduce material usage and print time, but greatly increases slicing time."
-msgstr ""
+msgstr "Genera un supporto tipo albero con rami di sostegno della stampa. Questo può ridurre l’impiego di materiale e il tempo di stampa, ma aumenta notevolmente il tempo di sezionamento."
#: fdmprinter.def.json
msgctxt "support_tree_angle label"
msgid "Tree Support Branch Angle"
-msgstr ""
+msgstr "Angolo ramo supporto ad albero"
#: fdmprinter.def.json
msgctxt "support_tree_angle description"
msgid "The angle of the branches. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach."
-msgstr ""
+msgstr "L’angolo dei rami. Utilizzare un angolo minore per renderli più verticali e più stabili. Utilizzare un angolo maggiore per avere una portata maggiore."
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance label"
msgid "Tree Support Branch Distance"
-msgstr ""
+msgstr "Distanza ramo supporto ad albero"
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance description"
msgid "How far apart the branches need to be when they touch the model. Making this distance small will cause the tree support to touch the model at more points, causing better overhang but making support harder to remove."
-msgstr ""
+msgstr "La distanza tra i rami necessaria quando toccano il modello. Una distanza ridotta causa il contatto del supporto ad albero con il modello in più punti, generando migliore sovrapposizione ma rendendo più difficoltosa la rimozione del supporto."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter label"
msgid "Tree Support Branch Diameter"
-msgstr ""
+msgstr "Diametro ramo supporto ad albero"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter description"
msgid "The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this."
-msgstr ""
+msgstr "Il diametro dei rami più sottili del supporto. I rami più spessi sono più resistenti. I rami verso la base avranno spessore maggiore."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle label"
msgid "Tree Support Branch Diameter Angle"
-msgstr ""
+msgstr "Angolo diametro ramo supporto ad albero"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle description"
msgid "The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the tree support."
-msgstr ""
+msgstr "L’angolo del diametro dei rami con il graduale ispessimento verso il fondo. Un angolo pari a 0 genera rami con spessore uniforme sull’intera lunghezza. Un angolo minimo può aumentare la stabilità del supporto ad albero."
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution label"
msgid "Tree Support Collision Resolution"
-msgstr ""
+msgstr "Risoluzione collisione supporto ad albero"
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution description"
msgid "Resolution to compute collisions with to avoid hitting the model. Setting this lower will produce more accurate trees that fail less often, but increases slicing time dramatically."
-msgstr ""
+msgstr "Risoluzione per calcolare le collisioni per evitare di colpire il modello. L’impostazione a un valore basso genera alberi più accurati che si rompono meno sovente, ma aumenta notevolmente il tempo di sezionamento."
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness label"
msgid "Tree Support Wall Thickness"
-msgstr ""
+msgstr "Spessore delle pareti supporto ad albero"
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness description"
msgid "The thickness of the walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "Lo spessore delle pareti dei rami del supporto ad albero. Le pareti più spesse hanno un tempo di stampa superiore ma non cadono facilmente."
#: fdmprinter.def.json
msgctxt "support_tree_wall_count label"
msgid "Tree Support Wall Line Count"
-msgstr ""
+msgstr "Numero delle linee perimetrali supporto ad albero"
#: fdmprinter.def.json
msgctxt "support_tree_wall_count description"
msgid "The number of walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "Il numero di pareti dei rami del supporto ad albero. Le pareti più spesse hanno un tempo di stampa superiore ma non cadono facilmente."
#: fdmprinter.def.json
msgctxt "slicing_tolerance label"
@@ -4386,7 +4380,7 @@ msgstr "Configurazione del rivestimento superficie superiore"
#: fdmprinter.def.json
msgctxt "roofing_pattern description"
msgid "The pattern of the top most layers."
-msgstr "Configurazione degli strati superiori."
+msgstr "Configurazione dei layers superiori."
#: fdmprinter.def.json
msgctxt "roofing_pattern option lines"
@@ -4411,17 +4405,17 @@ msgstr "Direzioni linea rivestimento superficie superiore"
#: fdmprinter.def.json
msgctxt "roofing_angles description"
msgid "A list of integer line directions to use when the top surface skin layers use the lines or zig zag pattern. 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)."
-msgstr "Un elenco di direzioni linee intere da usare quando gli strati rivestimento superficie superiore utilizzano le linee o la configurazione zig zag. Gli elementi dall’elenco sono utilizzati in sequenza con il progredire degli strati e, al raggiungimento della fine dell’elenco, la sequenza ricomincia dall’inizio. Le voci elencate sono separate da virgole e l’intero elenco è racchiuso tra parentesi quadre. L’elenco predefinito è vuoto, vale a dire che utilizza i valori angolari predefiniti (45 e 135 gradi)."
+msgstr "Un elenco di direzioni linee intere da usare quando i layers rivestimento superficie superiore utilizzano le linee o la configurazione zig zag. Gli elementi dall’elenco sono utilizzati in sequenza con il progredire dei layers e, al raggiungimento della fine dell’elenco, la sequenza ricomincia dall’inizio. Le voci elencate sono separate da virgole e l’intero elenco è racchiuso tra parentesi quadre. L’elenco predefinito è vuoto, vale a dire che utilizza i valori angolari predefiniti (45 e 135 gradi)."
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization label"
msgid "Infill Travel Optimization"
-msgstr ""
+msgstr "Ottimizzazione spostamenti riempimento"
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization description"
msgid "When enabled, the order in which the infill lines are printed is optimized to reduce the distance travelled. The reduction in travel time achieved very much depends on the model being sliced, infill pattern, density, etc. Note that, for some models that have many small areas of infill, the time to slice the model may be greatly increased."
-msgstr ""
+msgstr "Quando abilitato, l’ordine di stampa delle linee di riempimento viene ottimizzato per ridurre la distanza percorsa. La riduzione del tempo di spostamento ottenuta dipende in particolare dal modello sezionato, dalla configurazione di riempimento, dalla densità, ecc. Si noti che, per alcuni modelli che hanno piccole aree di riempimento, il tempo di sezionamento del modello può aumentare notevolmente."
#: fdmprinter.def.json
msgctxt "material_flow_dependent_temperature label"
@@ -4431,7 +4425,7 @@ msgstr "Temperatura automatica"
#: fdmprinter.def.json
msgctxt "material_flow_dependent_temperature description"
msgid "Change the temperature for each layer automatically with the average flow speed of that layer."
-msgstr "Modifica automaticamente la temperatura per ciascuno strato con la velocità media del flusso per tale strato."
+msgstr "Modifica automaticamente la temperatura per ciascun layer con la velocità media del flusso per tale strato."
#: fdmprinter.def.json
msgctxt "material_flow_temp_graph label"
@@ -4931,7 +4925,7 @@ msgstr "Ritardo tra due segmenti orizzontali WP"
#: fdmprinter.def.json
msgctxt "wireframe_flat_delay description"
msgid "Delay time between two horizontal segments. Introducing such a delay can cause better adhesion to previous layers at the connection points, while too long delays cause sagging. Only applies to Wire Printing."
-msgstr "Indica il tempo di ritardo tra due segmenti orizzontali. Introducendo un tale ritardo si può ottenere una migliore adesione agli strati precedenti in corrispondenza dei punti di collegamento, mentre ritardi troppo prolungati provocano cedimenti. Applicabile solo alla funzione Wire Printing."
+msgstr "Indica il tempo di ritardo tra due segmenti orizzontali. Introducendo un tale ritardo si può ottenere una migliore adesione ai layers precedenti in corrispondenza dei punti di collegamento, mentre ritardi troppo prolungati provocano cedimenti. Applicabile solo alla funzione Wire Printing."
#: fdmprinter.def.json
msgctxt "wireframe_up_half_speed label"
@@ -4943,9 +4937,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"
@@ -4955,7 +4947,7 @@ msgstr "Dimensione dei nodi WP"
#: fdmprinter.def.json
msgctxt "wireframe_top_jump description"
msgid "Creates a small knot at the top of an upward line, so that the consecutive horizontal layer has a better chance to connect to it. Only applies to Wire Printing."
-msgstr "Crea un piccolo nodo alla sommità di una linea verticale verso l'alto, in modo che lo strato orizzontale consecutivo abbia una migliore possibilità di collegarsi ad essa. Applicabile solo alla funzione Wire Printing."
+msgstr "Crea un piccolo nodo alla sommità di una linea verticale verso l'alto, in modo che il layer orizzontale consecutivo abbia una migliore possibilità di collegarsi ad essa. Applicabile solo alla funzione Wire Printing."
#: fdmprinter.def.json
msgctxt "wireframe_fall_down label"
@@ -4985,7 +4977,7 @@ msgstr "Strategia WP"
#: fdmprinter.def.json
msgctxt "wireframe_strategy description"
msgid "Strategy for making sure two consecutive layers connect at each connection point. Retraction lets the upward lines harden in the right position, but may cause filament grinding. A knot can be made at the end of an upward line to heighten the chance of connecting to it and to let the line cool; however, it may require slow printing speeds. Another strategy is to compensate for the sagging of the top of an upward line; however, the lines won't always fall down as predicted."
-msgstr "Strategia per garantire il collegamento di due strati consecutivi ad ogni punto di connessione. La retrazione consente l'indurimento delle linee verticali verso l'alto nella giusta posizione, ma può causare la deformazione del filamento. È possibile realizzare un nodo all'estremità di una linea verticale verso l'alto per accrescere la possibilità di collegamento e lasciarla raffreddare; tuttavia ciò può richiedere velocità di stampa ridotte. Un'altra strategia consiste nel compensare il cedimento della parte superiore di una linea verticale verso l'alto; tuttavia le linee non sempre ricadono come previsto."
+msgstr "Strategia per garantire il collegamento di due layers consecutivi ad ogni punto di connessione. La retrazione consente l'indurimento delle linee verticali verso l'alto nella giusta posizione, ma può causare la deformazione del filamento. È possibile realizzare un nodo all'estremità di una linea verticale verso l'alto per accrescere la possibilità di collegamento e lasciarla raffreddare; tuttavia ciò può richiedere velocità di stampa ridotte. Un'altra strategia consiste nel compensare il cedimento della parte superiore di una linea verticale verso l'alto; tuttavia le linee non sempre ricadono come previsto."
#: fdmprinter.def.json
msgctxt "wireframe_strategy option compensate"
@@ -5055,42 +5047,42 @@ msgstr "Indica la distanza tra l'ugello e le linee diagonali verso il basso. Un
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled label"
msgid "Use adaptive layers"
-msgstr ""
+msgstr "Uso di layers adattivi"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled description"
msgid "Adaptive layers computes the layer heights depending on the shape of the model."
-msgstr ""
+msgstr "I layers adattivi calcolano l’altezza dei layers in base alla forma del modello."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation label"
msgid "Adaptive layers maximum variation"
-msgstr ""
+msgstr "Variazione massima layers adattivi"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation description"
msgid "The maximum allowed height different from the base layer height in mm."
-msgstr ""
+msgstr "La differenza di altezza massima rispetto all’altezza del layer di base in mm."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step label"
msgid "Adaptive layers variation step size"
-msgstr ""
+msgstr "Dimensione variazione layers adattivi"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step description"
msgid "The difference in height of the next layer height compared to the previous one."
-msgstr ""
+msgstr "La differenza in altezza del layer successivo rispetto al precedente."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold label"
msgid "Adaptive layers threshold"
-msgstr ""
+msgstr "Soglia layers adattivi"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold description"
msgid "Threshold whether to use a smaller layer or not. This number is compared to the tan of the steepest slope in a layer."
-msgstr ""
+msgstr "Soglia per l’utilizzo o meno di un layer di dimensioni minori. Questo numero è confrontato al valore dell’inclinazione più ripida di un layer."
#: fdmprinter.def.json
msgctxt "command_line_settings label"
@@ -5100,7 +5092,7 @@ msgstr "Impostazioni riga di comando"
#: fdmprinter.def.json
msgctxt "command_line_settings description"
msgid "Settings which are only used if CuraEngine isn't called from the Cura frontend."
-msgstr "Impostazioni utilizzate solo se CuraEngine non è chiamato dalla parte anteriore di Cura."
+msgstr "Impostazioni utilizzate solo se CuraEngine non è chiamato dal frontend di Cura."
#: fdmprinter.def.json
msgctxt "center_object label"
@@ -5178,7 +5170,7 @@ msgstr "Matrice di rotazione da applicare al modello quando caricato dal file."
#~ msgctxt "infill_pattern description"
#~ msgid "The pattern of the infill material of the print. The line and zig zag infill swap direction on alternate layers, reducing material cost. The grid, triangle, cubic, octet, quarter cubic and concentric patterns are fully printed every layer. Cubic, quarter cubic and octet infill change with every layer to provide a more equal distribution of strength over each direction."
-#~ msgstr "Configurazione del materiale di riempimento della stampa. Il riempimento a linea e zig zag su strati alternati riduce il costo del materiale. Le configurazioni a griglia, triangolo, a cubo, ottagonale, a quarto di cubo e concentrica comportano la stampa completa in ogni strato. Il riempimento a cubi, a quarto di cubo e a ottagonale cambia a ogni strato per consentire una distribuzione più uniforme della resistenza in ogni direzione."
+#~ msgstr "Configurazione del materiale di riempimento della stampa. Il riempimento a linea e zig zag su layers alternati riduce il costo del materiale. Le configurazioni a griglia, triangolo, a cubo, ottagonale, a quarto di cubo e concentrica comportano la stampa completa in ogni layer. Il riempimento a cubi, a quarto di cubo e a ottagonale cambia a ogni layer per consentire una distribuzione più uniforme della resistenza in ogni direzione."
#~ msgctxt "zig_zaggify_infill description"
#~ msgid "Connect the ends where the infill pattern meets the inner wall using a lines which follows the shape of the inner wall. Enabling this setting can make the infill adhere to the walls better and reduces the effects on infill on the quality of vertical surfaces. Disabling this setting reduces the amount of material used."
@@ -5194,19 +5186,19 @@ msgstr "Matrice di rotazione da applicare al modello quando caricato dal file."
#~ msgctxt "z_offset_layer_0 label"
#~ msgid "Initial Layer Z Offset"
-#~ msgstr "Scostamento Z strato iniziale"
+#~ msgstr "Scostamento Z layer iniziale"
#~ msgctxt "z_offset_layer_0 description"
#~ msgid "The extruder is offset from the normal height of the first layer by this amount. It can be positive (raised) or negative (lowered). Some filament types adhere to the build plate better if the extruder is raised slightly."
-#~ msgstr "L'estrusore viene posizionato ad una distanza dall'altezza normale del primo strato pari al valore indicato. Questo scostamento può essere positivo (più in alto) o negativo (più in basso). Alcuni tipi di filamento aderiscono meglio al piano di stampa se l'estrusore viene leggermente sollevato."
+#~ msgstr "L'estrusore viene posizionato ad una distanza dall'altezza normale del primo layer pari al valore indicato. Questo scostamento può essere positivo (più in alto) o negativo (più in basso). Alcuni tipi di filamento aderiscono meglio al piano di stampa se l'estrusore viene leggermente sollevato."
#~ msgctxt "z_offset_taper_layers label"
#~ msgid "Z Offset Taper Layers"
-#~ msgstr "Scostamento Z strati di rastremazione"
+#~ msgstr "Scostamento Z layers di rastremazione"
#~ msgctxt "z_offset_taper_layers description"
#~ msgid "When non-zero, the Z offset is reduced to 0 over that many layers. A value of 0 means that the Z offset remains constant for all the layers in the print."
-#~ msgstr "Se diverso da zero, lo scostamento Z viene ridotto a 0 entro il numero di strati indicato. Un valore di 0 indica che lo scostamento Z rimane costante per tutti gli strati di stampa."
+#~ msgstr "Se diverso da zero, lo scostamento Z viene ridotto a 0 entro il numero di layers indicato. Un valore di 0 indica che lo scostamento Z rimane costante per tutti i layers di stampa."
#~ msgctxt "raft_smoothing description"
#~ msgid "This setting control how much inner corners in the raft outline are rounded. Inward corners are rounded to a semi circle with a radius equal to the value given here. This setting also removes holes in the raft outline which are smaller than such a circle."
@@ -5214,7 +5206,7 @@ msgstr "Matrice di rotazione da applicare al modello quando caricato dal file."
#~ msgctxt "infill_pattern description"
#~ msgid "The pattern of the infill material of the print. The line and zig zag infill swap direction on alternate layers, reducing material cost. The grid, triangle, cubic, tetrahedral and concentric patterns are fully printed every layer. Cubic and tetrahedral infill change with every layer to provide a more equal distribution of strength over each direction."
-#~ msgstr "Indica la configurazione del materiale di riempimento della stampa. Il riempimento a linea e a zig zag cambia direzione su strati alternati, riducendo il costo del materiale. Le configurazioni a griglia, triangolo, cubo, tetraedriche e concentriche sono stampate completamente su ogni strato. Il riempimento delle configurazioni cubiche e tetraedriche cambia ad ogni strato per fornire una distribuzione più uniforme della forza su ciascuna direzione."
+#~ msgstr "Indica la configurazione del materiale di riempimento della stampa. Il riempimento a linea e a zig zag cambia direzione su layers alternati, riducendo il costo del materiale. Le configurazioni a griglia, triangolo, cubo, tetraedriche e concentriche sono stampate completamente su ogni layers. Il riempimento delle configurazioni cubiche e tetraedriche cambia ad ogni strato per fornire una distribuzione più uniforme della forza su ciascuna direzione."
#~ msgctxt "infill_pattern option tetrahedral"
#~ msgid "Tetrahedral"
@@ -5222,27 +5214,27 @@ msgstr "Matrice di rotazione da applicare al modello quando caricato dal file."
#~ msgctxt "expand_skins_into_infill label"
#~ msgid "Expand Skins Into Infill"
-#~ msgstr "Prolunga rivestimenti esterni nel riempimento"
+#~ msgstr "Estende rivestimenti esterni nel riempimento"
#~ msgctxt "expand_skins_into_infill description"
#~ msgid "Expand skin areas of top and/or bottom skin of flat surfaces. By default, skins stop under the wall lines that surround infill but this can lead to holes appearing when the infill density is low. This setting extends the skins beyond the wall lines so that the infill on the next layer rests on skin."
-#~ msgstr "Prolunga le aree di rivestimento esterno superiori e/o inferiori delle superfici piatte. Per default, i rivestimenti esterni si interrompono sotto le linee delle pareti circostanti il riempimento, ma questo può generare la comparsa di fori quando la densità del riempimento è bassa. Questa impostazione prolunga i rivestimenti esterni oltre le linee delle pareti in modo che il riempimento sullo strato successivo appoggi sul rivestimento esterno."
+#~ msgstr "Estende le aree di rivestimento esterno superiori e/o inferiori delle superfici piatte. Per default, i rivestimenti esterni si interrompono sotto le linee delle pareti circostanti il riempimento, ma questo può generare la comparsa di fori quando la densità del riempimento è bassa. Questa impostazione prolunga i rivestimenti esterni oltre le linee delle pareti in modo che il riempimento sullo strato successivo appoggi sul rivestimento esterno."
#~ msgctxt "expand_upper_skins label"
#~ msgid "Expand Top Skins Into Infill"
-#~ msgstr "Prolunga rivestimenti esterni superiori nel riempimento"
+#~ msgstr "Estendi rivestimenti esterni superiori nel riempimento"
#~ msgctxt "expand_upper_skins description"
#~ msgid "Expand the top skin areas (areas with air above) so that they support infill above."
-#~ msgstr "Prolunga le aree di rivestimento esterno superiori (aree con aria al di sopra) in modo che supportino il riempimento sovrastante."
+#~ msgstr "Estendi le aree di rivestimento esterno superiori (aree con aria al di sopra) in modo che supportino il riempimento sovrastante."
#~ msgctxt "expand_lower_skins label"
#~ msgid "Expand Bottom Skins Into Infill"
-#~ msgstr "Prolunga rivestimenti esterni inferiori nel riempimento"
+#~ msgstr "Estendi rivestimenti esterni inferiori nel riempimento"
#~ msgctxt "expand_lower_skins description"
#~ msgid "Expand the bottom skin areas (areas with air below) so that they are anchored by the infill layers above and below."
-#~ msgstr "Prolunga aree rivestimento esterno inferiori (aree con aria al di sotto) in modo che siano ancorate dagli strati di riempimento sovrastanti e sottostanti."
+#~ msgstr "Estendi aree rivestimento esterno inferiori (aree con aria al di sotto) in modo che siano ancorate dai layers di riempimento sovrastanti e sottostanti."
#~ msgctxt "expand_skins_expand_distance description"
#~ msgid "The distance the skins are expanded into the infill. The default distance is enough to bridge the gap between the infill lines and will stop holes appearing in the skin where it meets the wall when the infill density is low. A smaller distance will often be sufficient."
@@ -5338,19 +5330,19 @@ msgstr "Matrice di rotazione da applicare al modello quando caricato dal file."
#~ msgctxt "expand_upper_skins label"
#~ msgid "Expand Upper Skins"
-#~ msgstr "Prolunga rivestimenti esterni superiori"
+#~ msgstr "Estendi rivestimenti esterni superiori"
#~ msgctxt "expand_upper_skins description"
#~ msgid "Expand upper skin areas (areas with air above) so that they support infill above."
-#~ msgstr "Prolunga le aree di rivestimento esterno superiori (aree con aria al di sopra) in modo che supportino il riempimento sovrastante."
+#~ msgstr "Estendi le aree di rivestimento esterno superiori (aree con aria al di sopra) in modo che supportino il riempimento sovrastante."
#~ msgctxt "expand_lower_skins label"
#~ msgid "Expand Lower Skins"
-#~ msgstr "Prolunga rivestimenti esterni inferiori"
+#~ msgstr "Estendi rivestimenti esterni inferiori"
#~ msgctxt "expand_lower_skins description"
#~ msgid "Expand lower skin areas (areas with air below) so that they are anchored by the infill layers above and below."
-#~ msgstr "Prolunga aree rivestimento esterno inferiori (aree con aria al di sotto) in modo che siano ancorate dagli strati di riempimento sovrastanti e sottostanti."
+#~ msgstr "Estendi aree rivestimento esterno inferiori (aree con aria al di sotto) in modo che siano ancorate dagli strati di riempimento sovrastanti e sottostanti."
#~ msgctxt "speed_support_interface description"
#~ msgid "The speed at which the roofs and bottoms of support are printed. Printing the them at lower speeds can improve overhang quality."
@@ -5374,7 +5366,7 @@ msgstr "Matrice di rotazione da applicare al modello quando caricato dal file."
#~ msgctxt "support_interface_extruder_nr description"
#~ msgid "The extruder train to use for printing the roofs and bottoms of the support. This is used in multi-extrusion."
-#~ msgstr "Il treno estrusore utilizzato per la stampa delle parti superiori e inferiori del supporto. Utilizzato nell’estrusione multipla."
+#~ msgstr "Il blocco estrusore utilizzato per la stampa delle parti superiori e inferiori del supporto. Utilizzato nell’estrusione multipla."
#~ msgctxt "support_bottom_stair_step_height description"
#~ msgid "The height of the steps of the stair-like bottom of support resting on the model. A low value makes the support harder to remove, but too high values can lead to unstable support structures."
@@ -5382,11 +5374,11 @@ msgstr "Matrice di rotazione da applicare al modello quando caricato dal file."
#~ msgctxt "support_bottom_height label"
#~ msgid "Support Bottom Thickness"
-#~ msgstr "Spessore degli strati inferiori del supporto"
+#~ msgstr "Spessore dei layers inferiori del supporto"
#~ msgctxt "support_bottom_height description"
#~ msgid "The thickness of the support bottoms. This controls the number of dense layers are printed on top of places of a model on which support rests."
-#~ msgstr "Indica lo spessore degli strati inferiori del supporto. Questo controlla il numero di strati fitti stampati sulla sommità dei punti di un modello su cui appoggia un supporto."
+#~ msgstr "Indica lo spessore dei layers inferiori del supporto. Questo controlla il numero di slayers fitti stampati sulla sommità dei punti di un modello su cui appoggia un supporto."
#~ msgctxt "support_interface_skip_height description"
#~ msgid "When checking where there's model above the support, take steps of the given height. Lower values will slice slower, while higher values may cause normal support to be printed in some places where there should have been support interface."
diff --git a/resources/i18n/ja_JP/cura.po b/resources/i18n/ja_JP/cura.po
index b394a3a510..11e6f08883 100644
--- a/resources/i18n/ja_JP/cura.po
+++ b/resources/i18n/ja_JP/cura.po
@@ -1,22 +1,22 @@
# Cura
-# Copyright (C) 2017 Ultimaker
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
-"PO-Revision-Date: 2017-11-30 13:05+0100\n"
-"Last-Translator: \n"
-"Language-Team: TEAM\n"
-"Language: xx_XX\n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
+"PO-Revision-Date: 2018-02-10 04:58+0900\n"
+"Last-Translator: Brule \n"
+"Language-Team: Japanese\n"
+"Language: ja_JP\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Poedit 1.8.7.1\n"
+"X-Generator: Poedit 2.0.4\n"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:26
msgctxt "@action"
@@ -191,7 +191,7 @@ msgstr "ファームウェア"
#: /home/ruben/Projects/Cura/plugins/PrepareStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Prepare"
-msgstr ""
+msgstr "準備する"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:23
msgctxt "@action:button Preceded by 'Ready to'."
@@ -564,7 +564,7 @@ msgstr "プリントジョブのインターフェイスをブラウザーで開
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:239
msgctxt "@label Printer name"
msgid "Unknown"
-msgstr ""
+msgstr "不明"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:505
#, python-brace-format
@@ -599,7 +599,7 @@ msgstr "ネットワーク上にて接続"
#: /home/ruben/Projects/Cura/plugins/MonitorStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Monitor"
-msgstr ""
+msgstr "モニター"
#: /home/ruben/Projects/Cura/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py:66
#, python-brace-format
@@ -626,7 +626,7 @@ msgstr "必要なアップデートの情報にアクセスできません。"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:579
msgctxt "@info:status"
msgid "SolidWorks reported errors, while opening your file. We recommend to solve these issues inside SolidWorks itself."
-msgstr ""
+msgstr "ソリッドワークスがファイルを開く際にエラーを報告しました。ソリッドワークス内で問題を解決することをお勧めします。"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:591
msgctxt "@info:status"
@@ -635,6 +635,9 @@ msgid ""
"\n"
" Thanks!."
msgstr ""
+"図面の中にモデルが見つかりません。中身を確認し、パートかアセンブリーが中に入っていることを確認してください。\n"
+"\n"
+" 再確認をお願いします。"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:595
msgctxt "@info:status"
@@ -643,6 +646,9 @@ msgid ""
"\n"
"Sorry!"
msgstr ""
+"図面の中にパートかアセンブリーが2個以上見つかりました。今のところ、本製品はパートかアセンブリーが1個の図面のみに対応しています。\n"
+"\n"
+"申し訳ありません。"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:25
msgctxt "@item:inlistbox"
@@ -657,7 +663,7 @@ msgstr "ソリッドワークスアセンブリーファイル"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:33
msgctxt "@item:inlistbox"
msgid "SolidWorks drawing file"
-msgstr ""
+msgstr "ソリッドワークス図面ファイル"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:48
msgctxt "@info:status"
@@ -668,6 +674,11 @@ msgid ""
"With kind regards\n"
" - Thomas Karl Pietrowski"
msgstr ""
+"お客様へ\n"
+"システム上に正規のソリッドワークスがインストールされていません。つまり、ソリッドワークスがインストールされていないか、有効なライセンスが存在しません。ソリッドワークスだけを問題なく使用できるようになっているか確認するか、自社のIT部門にご相談ください。\n"
+"\n"
+"お願いいたします。\n"
+" - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:57
msgctxt "@info:status"
@@ -678,6 +689,11 @@ msgid ""
"With kind regards\n"
" - Thomas Karl Pietrowski"
msgstr ""
+"お客様へ\n"
+"このプラグインは現在Windows以外のOSで実行されています。このプラグインは、ソリッドワークスがインストールされたWindowsでしか動作しません。有効なライセンスも必要です。ソリッドワークスがインストールされたWindowsマシンにこのプラグインをインストールしてください。\n"
+"\n"
+"お願いいたします。\n"
+" - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:70
msgid "Configure"
@@ -685,7 +701,7 @@ msgstr "構成"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:71
msgid "Installation guide for SolidWorks macro"
-msgstr ""
+msgstr "ソリッドワークス・マクロのインストールガイド"
#: /home/ruben/Projects/Cura/plugins/SimulationView/__init__.py:14
msgctxt "@item:inlistbox"
@@ -709,7 +725,7 @@ msgstr "G-codeを修正"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:43
msgctxt "@info"
msgid "Cura collects anonymized usage statistics."
-msgstr ""
+msgstr "Curaは、匿名化した利用統計を収集します。"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:46
msgctxt "@info:title"
@@ -719,22 +735,22 @@ msgstr "データを収集中"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:48
msgctxt "@action:button"
msgid "Allow"
-msgstr ""
+msgstr "許可"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:49
msgctxt "@action:tooltip"
msgid "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing."
-msgstr ""
+msgstr "Curaが匿名化した利用統計を送信することを許可し、Curaの将来の改善を優先的に行うことに貢献します。プレファレンスと設定の一部、Curaのバージョン、スライスしているモデルのハッシュが送信されます。"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:50
msgctxt "@action:button"
msgid "Disable"
-msgstr ""
+msgstr "無効化"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:51
msgctxt "@action:tooltip"
msgid "Don't allow Cura to send anonymized usage statistics. You can enable it again in the preferences."
-msgstr ""
+msgstr "Curaが匿名化した利用統計を送信することを許可しません。プレファレンスで許可に変更することができます。"
#: /home/ruben/Projects/Cura/plugins/LegacyProfileReader/__init__.py:14
msgctxt "@item:inlistbox"
@@ -744,7 +760,7 @@ msgstr "Cura 15.04 プロファイル"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/__init__.py:15
msgctxt "@item:inlistbox"
msgid "Blender file"
-msgstr ""
+msgstr "Blenderファイル"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/CadIntegrationUtils/CommonReader.py:199
msgctxt "@info:status"
@@ -752,6 +768,8 @@ msgid ""
"Could not export using \"{}\" quality!\n"
"Felt back to \"{}\"."
msgstr ""
+"\"{}\"品質を使用したエクスポートができませんでした!\n"
+"\"{}\"になりました。"
#: /home/ruben/Projects/Cura/plugins/GCodeProfileReader/__init__.py:14
#: /home/ruben/Projects/Cura/plugins/GCodeReader/__init__.py:14
@@ -938,12 +956,12 @@ msgstr "Curaプロファイル"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:12
msgctxt "@item:inmenu"
msgid "Profile Assistant"
-msgstr ""
+msgstr "プロファイルアシスタント"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:17
msgctxt "@item:inlistbox"
msgid "Profile Assistant"
-msgstr ""
+msgstr "プロファイルアシスタント"
#: /home/ruben/Projects/Cura/plugins/3MFWriter/__init__.py:30
msgctxt "@item:inlistbox"
@@ -1141,13 +1159,13 @@ msgstr "{0}: {1}からプロファイル
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "This profile {0} contains incorrect data, could not import it."
-msgstr ""
+msgstr "このプロファイル{0}には、正しくないデータが含まれていて、インポートできません。"
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:240
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "The machine defined in profile {0} doesn't match with your current machine, could not import it."
-msgstr ""
+msgstr "プロファイル{0}の中で定義されているマシンは、現在お使いのマシンと一致しませんので、インポートできませんでした。"
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:274
#, python-brace-format
@@ -1159,7 +1177,7 @@ msgstr "プロファイル {0}の取り込み完了"
#, python-brace-format
msgctxt "@info:status"
msgid "File {0} does not contain any valid profile."
-msgstr ""
+msgstr "ファイル{0}には、正しいプロファイルが含まれていません。"
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:280
#, python-brace-format
@@ -1187,7 +1205,7 @@ msgstr "進行中のプリント構成にあったクオリティータイプ{0}
#, python-brace-format
msgctxt "@label"
msgid "Group #{group_nr}"
-msgstr ""
+msgstr "グループ #{group_nr}"
#: /home/ruben/Projects/Cura/cura/BuildVolume.py:100
msgctxt "@info:status"
@@ -1247,6 +1265,9 @@ msgid ""
"
Please use the \"Send report\" button to post a bug report automatically to our servers
"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:147
msgctxt "@title:groupbox"
msgid "Error traceback"
-msgstr ""
+msgstr "오류 추적"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:214
msgctxt "@title:groupbox"
@@ -1343,7 +1343,7 @@ msgstr "인터페이스로드 중 ..."
#, python-format
msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be translated; just translate the format of ##x##x## mm."
msgid "%(width).1f x %(depth).1f x %(height).1f mm"
-msgstr "% (너비) .1f x % (깊이) .1f x % (높이) .1f mm"
+msgstr "%(width).1f x %(depth).1f x %(height).1f mm"
#: /home/ruben/Projects/Cura/cura/CuraApplication.py:1417
#, python-brace-format
@@ -1518,7 +1518,7 @@ msgstr "노즐 크기"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:393
msgctxt "@label"
msgid "Compatible material diameter"
-msgstr ""
+msgstr "호환이 되는 재료 직경"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:395
msgctxt "@tooltip"
@@ -1620,10 +1620,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 ""
-":네트워크를 통해 프린터로 직접 인쇄하려면 네트워크 케이블을 사용하거나 프린터를 WIFI 네트워크에 연결하여 프린터가 네트워크에 연결되어 있는지 확인하십시오. Cura를 프린터에 연결하지 않은 경우에도 USB 드라이브를 사용하여 g 코드 파일을 프린터로 전송할 수 있습니다\n"
-"\n"
-"아래 목록에서 프린터를 선택하십시오"
+msgstr ":네트워크를 통해 프린터로 직접 인쇄하려면 네트워크 케이블을 사용하거나 프린터를 WIFI 네트워크에 연결하여 프린터가 네트워크에 연결되어 있는지 확인하십시오. Cura를 프린터에 연결하지 않은 경우에도 USB 드라이브를 사용하여 g 코드 파일을 프린터로 전송할 수 있습니다\n\n아래 목록에서 프린터를 선택하십시오"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:75
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:44
@@ -1663,12 +1660,12 @@ msgstr "유형"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:233
msgctxt "@label Printer name"
msgid "Ultimaker 3"
-msgstr ""
+msgstr "Ultimaker 3"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:236
msgctxt "@label Printer name"
msgid "Ultimaker 3 Extended"
-msgstr ""
+msgstr "Ultimaker 3 Extended"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:252
msgctxt "@label"
@@ -1735,7 +1732,7 @@ msgstr "?은 연결된 Ultimaker 3에 연결된 프린터 그룹을 호스트하
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml:55
msgctxt "@label link to connect manager"
msgid "Add/Remove printers"
-msgstr ""
+msgstr "프린터 추가/제거"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/OpenPanelButton.qml:14
msgctxt "@info:tooltip"
@@ -1775,7 +1772,7 @@ msgstr "프린터와의 연결이 끊어졌습니다"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:47
msgctxt "@label Printer status"
msgid "Unknown"
-msgstr ""
+msgstr "알 수 없음"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:257
msgctxt "@label:status"
@@ -1871,164 +1868,164 @@ msgstr "구성 활성화"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:21
msgctxt "@title:window"
msgid "SolidWorks: Export wizard"
-msgstr ""
+msgstr "SolidWorks: 내보내기 마법사"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:45
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:140
msgctxt "@action:label"
msgid "Quality:"
-msgstr ""
+msgstr "품질:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:78
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:179
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (3D-printing)"
-msgstr ""
+msgstr "고품질 (3D-프린팅)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:79
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:180
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (3D-printing)"
-msgstr ""
+msgstr "저품질 (3D-프린팅)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:80
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:181
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (SolidWorks)"
-msgstr ""
+msgstr "고품질 (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:81
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:182
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (SolidWorks)"
-msgstr ""
+msgstr "저품질 (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:94
msgctxt "@text:window"
msgid "Show this dialog again"
-msgstr ""
+msgstr "이 대화창을 다시 표시"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:104
msgctxt "@action:button"
msgid "Continue"
-msgstr ""
+msgstr "계속"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:116
msgctxt "@action:button"
msgid "Abort"
-msgstr ""
+msgstr "중단"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:21
msgctxt "@title:window"
msgid "How to install Cura SolidWorks macro"
-msgstr ""
+msgstr "Cura SolidWorks 매크로 설치 방법"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:62
msgctxt "@description:label"
msgid "Steps:"
-msgstr ""
+msgstr "단계:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:140
msgctxt "@action:button"
msgid ""
"Open the directory\n"
"with macro and icon"
-msgstr ""
+msgstr "매크로와 아이콘으로\n디렉토리 열기"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:160
msgctxt "@description:label"
msgid "Instructions:"
-msgstr ""
+msgstr "안내:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:202
msgctxt "@action:playpause"
msgid "Play"
-msgstr ""
+msgstr "재생"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:206
msgctxt "@action:playpause"
msgid "Pause"
-msgstr ""
+msgstr "중지"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:268
msgctxt "@action:button"
msgid "Previous Step"
-msgstr ""
+msgstr "이전 단계"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:283
msgctxt "@action:button"
msgid "Done"
-msgstr ""
+msgstr "완료"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:287
msgctxt "@action:button"
msgid "Next Step"
-msgstr ""
+msgstr "다음 단계"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:21
msgctxt "@title:window"
msgid "SolidWorks plugin: Configuration"
-msgstr ""
+msgstr "SolidWorks 플러그인: 구성"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:39
msgctxt "@title:tab"
msgid "Conversion settings"
-msgstr ""
+msgstr "변환 설정"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:66
msgctxt "@label"
msgid "First choice:"
-msgstr ""
+msgstr "첫 번째 선택:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:86
msgctxt "@text:menu"
msgid "Latest installed version (Recommended)"
-msgstr ""
+msgstr "가장 최근 설치 버전(권장)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:95
msgctxt "@text:menu"
msgid "Default version"
-msgstr ""
+msgstr "기본 버전"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:193
msgctxt "@label"
msgid "Show wizard before opening SolidWorks files"
-msgstr ""
+msgstr "SolidWorks 파일을 열기 전 마법사 표시"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:203
msgctxt "@label"
msgid "Automatically rotate opened file into normed orientation"
-msgstr ""
+msgstr "열린 파일을 규정된 방향으로 자동 회전"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:210
msgctxt "@title:tab"
msgid "Installation(s)"
-msgstr ""
+msgstr "설치"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:284
msgctxt "@label"
msgid "COM service found"
-msgstr ""
+msgstr "COM 서비스 발견"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:295
msgctxt "@label"
msgid "Executable found"
-msgstr ""
+msgstr "실행가능 발견"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:306
msgctxt "@label"
msgid "COM starting"
-msgstr ""
+msgstr "COM 시작"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:317
msgctxt "@label"
msgid "Revision number"
-msgstr ""
+msgstr "수정 번호"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:328
msgctxt "@label"
msgid "Functions available"
-msgstr ""
+msgstr "사용 가능한 기능"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:341
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:264
@@ -2214,32 +2211,32 @@ msgstr "부드럽게하기"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:38
msgctxt "@label"
msgid "Mesh Type"
-msgstr ""
+msgstr "메쉬 유형"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:69
msgctxt "@label"
msgid "Normal model"
-msgstr ""
+msgstr "일반 모델"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:76
msgctxt "@label"
msgid "Print as support"
-msgstr ""
+msgstr "서포터로 프린팅"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:84
msgctxt "@label"
msgid "Don't support overlap with other models"
-msgstr ""
+msgstr "다른 모델과 오버랩되도록 지지하지 않음"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:92
msgctxt "@label"
msgid "Modify settings for overlap with other models"
-msgstr ""
+msgstr "다른 모델과의 오버랩에 대한 설정 수정"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:100
msgctxt "@label"
msgid "Modify settings for infill of other models"
-msgstr ""
+msgstr "다른 모델의 충진재에 대한 설정 수정"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:333
msgctxt "@action:button"
@@ -2425,10 +2422,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 ""
-"이 플러그인에는 라이선스가 포함되어 있습니다.\n"
-"이 플러그인을 설치하려면 이 라이선스를 수락해야 합니다.\n"
-"아래의 약관에 동의하시겠습니까?"
+msgstr "이 플러그인에는 라이선스가 포함되어 있습니다.\n이 플러그인을 설치하려면 이 라이선스를 수락해야 합니다.\n아래의 약관에 동의하시겠습니까?"
#: /home/ruben/Projects/Cura/plugins/PluginBrowser/PluginBrowser.qml:242
msgctxt "@action:button"
@@ -2693,9 +2687,7 @@ msgctxt "@text:window"
msgid ""
"You have customized some profile settings.\n"
"Would you like to keep or discard those settings?"
-msgstr ""
-"일부 프로필 설정을 사용자 지정했습니다.\n"
-"이러한 설정을 유지하거나 삭제 하시겠습니까?"
+msgstr "일부 프로필 설정을 사용자 지정했습니다.\n이러한 설정을 유지하거나 삭제 하시겠습니까?"
#: /home/ruben/Projects/Cura/resources/qml/DiscardOrKeepProfileChangesDialog.qml:110
msgctxt "@title:column"
@@ -3099,27 +3091,27 @@ msgstr "인쇄 (익명) 인쇄 정보"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:674
msgctxt "@label"
msgid "Experimental"
-msgstr ""
+msgstr "실험적"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:680
msgctxt "@info:tooltip"
msgid "Use multi build plate functionality"
-msgstr ""
+msgstr "다수의 빌드 플레이트를 기능적으로 사용하기"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:685
msgctxt "@option:check"
msgid "Use multi build plate functionality (restart required)"
-msgstr ""
+msgstr "다수의 빌드 플레이트 기능성 사용(다시 시작해야 합니다)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:694
msgctxt "@info:tooltip"
msgid "Should newly loaded models be arranged on the build plate? Used in conjunction with multi build plate (EXPERIMENTAL)"
-msgstr ""
+msgstr "새롭게 로드한 모델을 빌드 플레이트에 정렬해야 합니까? 다수의 빌드 플레이트와 연계하여 사용(실험 중)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:699
msgctxt "@option:check"
msgid "Do not arrange objects on load"
-msgstr ""
+msgstr "로드 중인 대상물을 정렬하지 마십시오"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:15
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:514
@@ -3363,9 +3355,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는 커뮤니티와 공동으로 Ultimaker B.V.에 의해 개발되었습니다\n"
-"Cura는 다음 오픈 소스 프로젝트를 자랑스럽게 사용합니다"
+msgstr "Cura는 커뮤니티와 공동으로 Ultimaker B.V.에 의해 개발되었습니다\nCura는 다음 오픈 소스 프로젝트를 자랑스럽게 사용합니다"
#: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:118
msgctxt "@label"
@@ -3473,10 +3463,7 @@ msgid ""
"Some setting/override values are different from the values stored in the profile.\n"
"\n"
"Click to open the profile manager."
-msgstr ""
-"일부 설정 / 대체 값은 프로파일에 저장된 값과 다릅니다.\n"
-"\n"
-"프로파일 매니저를 열려면 클릭하십시오."
+msgstr "일부 설정 / 대체 값은 프로파일에 저장된 값과 다릅니다.\n\n프로파일 매니저를 열려면 클릭하십시오."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:150
msgctxt "@label:textbox"
@@ -3514,10 +3501,7 @@ msgid ""
"Some hidden settings use values different from their normal calculated value.\n"
"\n"
"Click to make these settings visible."
-msgstr ""
-"일부 숨겨진 설정은 정상 계산 값과 다른 값을 사용합니다.\n"
-"\n"
-"이 설정을 표시하려면 클릭하십시오."
+msgstr "일부 숨겨진 설정은 정상 계산 값과 다른 값을 사용합니다.\n\n이 설정을 표시하려면 클릭하십시오."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:61
msgctxt "@label Header for list of settings."
@@ -3532,7 +3516,7 @@ msgstr "영향을 받다"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:156
msgctxt "@label"
msgid "This setting is always shared between all extruders. Changing it here will change the value for all extruders."
-msgstr ""
+msgstr "이 설정은 항상 모든 압출기 사이에 공유됩니다. 여기서 변경하면 모든 압출기에 대한 값이 변경됩니다."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:159
msgctxt "@label"
@@ -3545,10 +3529,7 @@ msgid ""
"This setting has a value that is different from the profile.\n"
"\n"
"Click to restore the value of the profile."
-msgstr ""
-"이 설정에는 프로필과 다른 값이 있습니다.\n"
-"\n"
-"프로필 값을 복원하려면 클릭하십시오."
+msgstr "이 설정에는 프로필과 다른 값이 있습니다.\n\n프로필 값을 복원하려면 클릭하십시오."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:288
msgctxt "@label"
@@ -3556,10 +3537,7 @@ msgid ""
"This setting is normally calculated, but it currently has an absolute value set.\n"
"\n"
"Click to restore the calculated value."
-msgstr ""
-"이 설정은 일반적으로 계산되지만 현재는 절대 값이 설정되어 있습니다.\n"
-"\n"
-"계산 된 값을 복원하려면 클릭하십시오."
+msgstr "이 설정은 일반적으로 계산되지만 현재는 절대 값이 설정되어 있습니다.\n\n계산 된 값을 복원하려면 클릭하십시오."
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:128
msgctxt "@label:listbox"
@@ -3571,9 +3549,7 @@ msgctxt "@label:listbox"
msgid ""
"Print Setup disabled\n"
"G-code files cannot be modified"
-msgstr ""
-"인쇄 설정 사용 안 함\n"
-"G 코드 파일은 수정할 수 없습니다"
+msgstr "인쇄 설정 사용 안 함\nG 코드 파일은 수정할 수 없습니다"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:342
msgctxt "@label Hours and minutes"
@@ -3583,7 +3559,7 @@ msgstr "00h 00min"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:359
msgctxt "@tooltip"
msgid "Time specification"
-msgstr ""
+msgstr "시간 사양"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:441
msgctxt "@label"
@@ -3620,16 +3596,12 @@ msgstr "%1m / ~ %2g"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:591
msgctxt "@tooltip"
msgid "Recommended Print Setup
Print with the recommended settings for the selected printer, material and quality."
-msgstr ""
-"권장 인쇄 설정\n"
-"선택한 프린터, 재질 및 품질에 대한 권장 설정으로 인쇄하십시오."
+msgstr "권장 인쇄 설정\n선택한 프린터, 재질 및 품질에 대한 권장 설정으로 인쇄하십시오."
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:596
msgctxt "@tooltip"
msgid "Custom Print Setup
Print with finegrained control over every last bit of the slicing process."
-msgstr ""
-"사용자 정의 인쇄 설정\n"
-"마지막 스플 라이스 프로세스의 모든 비트를 미세하게 제어하여 인쇄하십시오."
+msgstr "사용자 정의 인쇄 설정\n마지막 스플 라이스 프로세스의 모든 비트를 미세하게 제어하여 인쇄하십시오."
#: /home/ruben/Projects/Cura/resources/qml/Menus/MaterialMenu.qml:50
msgctxt "@title:menuitem %1 is the automatically selected material"
@@ -3644,12 +3616,12 @@ msgstr "조망"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:37
msgctxt "@action:inmenu menubar:view"
msgid "&Camera position"
-msgstr ""
+msgstr "카메라 위치(&C)"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:52
msgctxt "@action:inmenu menubar:view"
msgid "&Build plate"
-msgstr ""
+msgstr "빌드 플레이트(&B)"
#: /home/ruben/Projects/Cura/resources/qml/Menus/NozzleMenu.qml:40
msgctxt "@title:menuitem %1 is the nozzle currently loaded in the printer"
@@ -3819,27 +3791,27 @@ msgstr "그만두다"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:114
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
-msgstr ""
+msgstr "3D 보기(&3)"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:121
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
-msgstr ""
+msgstr "앞에서 보기(&F)"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:128
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
-msgstr ""
+msgstr "위에서 보기(&T)"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:135
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
-msgstr ""
+msgstr "왼쪽에서 보기(&L)"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:142
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
-msgstr ""
+msgstr "오른쪽에서 보기(&R)"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:149
msgctxt "@action:inmenu"
@@ -3965,7 +3937,7 @@ msgstr "모든 모델 새로고치"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:edit"
msgid "Arrange All Models To All Build Plates"
-msgstr ""
+msgstr "모든 모델을 모든 빌드 플레이트에 정렬"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:358
msgctxt "@action:inmenu menubar:edit"
@@ -4025,7 +3997,7 @@ msgstr "설치된 플러그인 ..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:438
msgctxt "@action:inmenu menubar:view"
msgid "Expand/Collapse Sidebar"
-msgstr ""
+msgstr "사이드바 확장/축소"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:26
msgctxt "@label:PrintjobStatus"
@@ -4060,12 +4032,12 @@ msgstr "슬라이스 사용 불가"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Slice current printjob"
-msgstr ""
+msgstr "현재 출력물 슬라이스"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Cancel slicing process"
-msgstr ""
+msgstr "슬라이싱 프로세스 취소"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:183
msgctxt "@label:Printjob"
@@ -4121,7 +4093,7 @@ msgstr "다른 이름으로 저장..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:139
msgctxt "@title:menu menubar:file"
msgid "Save &Project..."
-msgstr ""
+msgstr "프로젝트 저장(&P)..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:162
msgctxt "@title:menu menubar:toplevel"
@@ -4313,9 +4285,7 @@ msgstr "테두리 또는 raft 인쇄를 사용합니다. 이렇게하면 개체
#: /home/ruben/Projects/Cura/resources/qml/SidebarSimple.qml:969
msgctxt "@label"
msgid "Need help improving your prints? Read the Ultimaker Troubleshooting Guides"
-msgstr ""
-"인쇄물 개선에 도움이 필요하십니까?\n"
-"Ultimaker 문제 해결 가이드 읽기"
+msgstr "인쇄물 개선에 도움이 필요하십니까?\nUltimaker 문제 해결 가이드 읽기"
#: /home/ruben/Projects/Cura/resources/qml/ExtruderButton.qml:16
msgctxt "@label %1 is filled in with the name of an extruder"
@@ -4362,7 +4332,7 @@ msgstr "자재"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:352
msgctxt "@label"
msgid "Check compatibility"
-msgstr ""
+msgstr "호환성 확인"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:372
msgctxt "@tooltip"
@@ -4372,17 +4342,17 @@ msgstr "Ultimaker.com의 재질 호환성을 확인하려면 클릭하십시오.
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:211
msgctxt "@option:check"
msgid "See only current build plate"
-msgstr ""
+msgstr "현재의 빌드 플레이트만 보기"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:227
msgctxt "@action:button"
msgid "Arrange to all build plates"
-msgstr ""
+msgstr "모든 빌드 플레이트 정렬"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:247
msgctxt "@action:button"
msgid "Arrange current build plate"
-msgstr ""
+msgstr "현재의 빌드 플레이트 정렬"
#: MachineSettingsAction/plugin.json
msgctxt "description"
@@ -4477,22 +4447,22 @@ msgstr "USB 인쇄"
#: PrepareStage/plugin.json
msgctxt "description"
msgid "Provides a prepare stage in Cura."
-msgstr ""
+msgstr "Cura에서 준비 단계 제공."
#: PrepareStage/plugin.json
msgctxt "name"
msgid "Prepare Stage"
-msgstr ""
+msgstr "준비 단계"
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "description"
msgid "Provides an edit window for direct script editing."
-msgstr ""
+msgstr "직접 스크립트 편집을 위한 편집창 제공."
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "name"
msgid "Live scripting tool"
-msgstr ""
+msgstr "라이브 스크립팅 도구"
#: RemovableDriveOutputDevice/plugin.json
msgctxt "description"
@@ -4517,12 +4487,12 @@ msgstr "UM3 네트워크 연결"
#: MonitorStage/plugin.json
msgctxt "description"
msgid "Provides a monitor stage in Cura."
-msgstr ""
+msgstr "Cura에서 모니터 단계 제공."
#: MonitorStage/plugin.json
msgctxt "name"
msgid "Monitor Stage"
-msgstr ""
+msgstr "모니터 단계"
#: FirmwareUpdateChecker/plugin.json
msgctxt "description"
@@ -4537,7 +4507,7 @@ msgstr "펌웨어 업데이트 검사기"
#: CuraSolidWorksPlugin/plugin.json
msgctxt "description"
msgid "Gives you the possibility to open certain files using SolidWorks itself. Conversion is done by this plugin and additional optimizations."
-msgstr ""
+msgstr "SolidWorks를 사용하여 특정 파일을 열 수 있는 가능성을 제공합니다. 이 플러그인과 추가 최적화로 변환을 수행합니다."
#: CuraSolidWorksPlugin/plugin.json
msgctxt "name"
@@ -4607,12 +4577,12 @@ msgstr "레거시 Cura 프로필 리더"
#: CuraBlenderPlugin/plugin.json
msgctxt "description"
msgid "Helps to open Blender files directly in Cura."
-msgstr ""
+msgstr "Cura에서 직접 Blender 파일을 열도록 도와줍니다."
#: CuraBlenderPlugin/plugin.json
msgctxt "name"
msgid "Blender Integration (experimental)"
-msgstr ""
+msgstr "Blender 통합(실험 중)"
#: GCodeProfileReader/plugin.json
msgctxt "description"
@@ -4777,12 +4747,12 @@ msgstr "Cura 프로필 작성자"
#: CuraPrintProfileCreator/plugin.json
msgctxt "description"
msgid "Allows material manufacturers to create new material and quality profiles using a drop-in UI."
-msgstr ""
+msgstr "재료 제조사가 드롭 인 UI를 사용하여 새로운 재료와 품질 프로필을 만들 수 있게 합니다."
#: CuraPrintProfileCreator/plugin.json
msgctxt "name"
msgid "Print Profile Assistant"
-msgstr ""
+msgstr "프린트 프로필 어시스턴트"
#: 3MFWriter/plugin.json
msgctxt "description"
diff --git a/resources/i18n/ko_KR/fdmprinter.def.json.po b/resources/i18n/ko_KR/fdmprinter.def.json.po
index 6b504c74de..36637151ca 100644
--- a/resources/i18n/ko_KR/fdmprinter.def.json.po
+++ b/resources/i18n/ko_KR/fdmprinter.def.json.po
@@ -350,12 +350,12 @@ msgstr "반복기"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract label"
msgid "Firmware Retraction"
-msgstr ""
+msgstr "펌웨어 제거"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract description"
msgid "Whether to use firmware retract commands (G10/G11) instead of using the E property in G1 commands to retract the material."
-msgstr ""
+msgstr "재료를 제거하는 G1 명령어에서 E 속성을 사용하는 대신 펌웨어 제거 명령어(G10/G11)를 사용할 지 여부."
#: fdmprinter.def.json
msgctxt "machine_disallowed_areas label"
@@ -1050,12 +1050,12 @@ msgstr "어디에나"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps label"
msgid "Filter Out Tiny Gaps"
-msgstr ""
+msgstr "아주 작은 갭 필터 아웃"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps description"
msgid "Filter out tiny gaps to reduce blobs on outside of model."
-msgstr ""
+msgstr "아주 작은 갭을 필터 아웃하여 모델 외부의 얼룩을 줄입니다."
#: fdmprinter.def.json
msgctxt "fill_outline_gaps label"
@@ -1440,7 +1440,7 @@ msgstr "충진 X 오프셋"
#: fdmprinter.def.json
msgctxt "infill_offset_x description"
msgid "The infill pattern is moved this distance along the X axis."
-msgstr ""
+msgstr "충진 패턴이 X축을 따라 이 거리만큼 이동합니다."
#: fdmprinter.def.json
msgctxt "infill_offset_y label"
@@ -1450,7 +1450,7 @@ msgstr "충진 Y 오프셋"
#: fdmprinter.def.json
msgctxt "infill_offset_y description"
msgid "The infill pattern is moved this distance along the Y axis."
-msgstr ""
+msgstr "충진 패턴이 Y축을 따라 이 거리만큼 이동합니다."
#: fdmprinter.def.json
msgctxt "sub_div_rad_add label"
@@ -1470,7 +1470,7 @@ msgstr "충진 오버랩 비율"
#: fdmprinter.def.json
msgctxt "infill_overlap description"
msgid "The amount of overlap between the infill and the walls as a percentage of the infill line width. A slight overlap allows the walls to connect firmly to the infill."
-msgstr ""
+msgstr "충진 라인 폭의 비율인 충진재와 벽 사이의 오버랩 양 약간의 오버랩으로 벽이 충진재와 확실하게 체결됩니다."
#: fdmprinter.def.json
msgctxt "infill_overlap_mm label"
@@ -1490,7 +1490,7 @@ msgstr "피부 겹침 비율"
#: fdmprinter.def.json
msgctxt "skin_overlap description"
msgid "The amount of overlap between the skin and the walls as a percentage of the skin line width. A slight overlap allows the walls to connect firmly to the skin. This is a percentage of the average line widths of the skin lines and the innermost wall."
-msgstr ""
+msgstr "스킨 라인 폭의 비율인 스킨과 벽 사이의 오버랩 양 약간의 오버랩으로 벽이 스킨과 확실하게 체결됩니다. 이것은 스킨 라인과 가장 안쪽 벽과의 평균 라인 폭의 비율입니다."
#: fdmprinter.def.json
msgctxt "skin_overlap_mm label"
@@ -1720,7 +1720,7 @@ msgstr "빌드 플레이트 온도"
#: fdmprinter.def.json
msgctxt "material_bed_temperature description"
msgid "The temperature used for the heated build plate. If this is 0, the bed temperature will not be adjusted."
-msgstr ""
+msgstr "가열 된 빌드 플레이트에 사용되는 온도. 이것이 0이면, 베드 온도가 조정되지 않습니다."
#: fdmprinter.def.json
msgctxt "material_bed_temperature_layer_0 label"
@@ -3502,9 +3502,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 ""
-"프린트의 스커트와 첫 번째 레이어 사이의 수직 거리입니다.\n"
-"이는 최소 거리입니다. 여러 개의 스커트 선이 이 거리에서 바깥쪽으로 연장됩니다."
+msgstr "프린트의 스커트와 첫 번째 레이어 사이의 수직 거리입니다.\n이는 최소 거리입니다. 여러 개의 스커트 선이 이 거리에서 바깥쪽으로 연장됩니다."
#: fdmprinter.def.json
msgctxt "skirt_brim_minimal_length label"
@@ -4264,82 +4262,82 @@ msgstr "실험적인 "
#: fdmprinter.def.json
msgctxt "support_tree_enable label"
msgid "Tree Support"
-msgstr ""
+msgstr "트리 지지물"
#: fdmprinter.def.json
msgctxt "support_tree_enable description"
msgid "Generate a tree-like support with branches that support your print. This may reduce material usage and print time, but greatly increases slicing time."
-msgstr ""
+msgstr "프린트물을 지지하는 가지가 달린 나무 모양의 지지물을 생성합니다. 이것이 재료 사용과 인쇄 시간을 줄여줄 수 있지만, 슬라이싱 시간이 상당히 늘어납니다."
#: fdmprinter.def.json
msgctxt "support_tree_angle label"
msgid "Tree Support Branch Angle"
-msgstr ""
+msgstr "트리 지지물 브랜치 각도"
#: fdmprinter.def.json
msgctxt "support_tree_angle description"
msgid "The angle of the branches. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach."
-msgstr ""
+msgstr "브랜치의 각도. 적은 각도를 사용하면 더 수직이 되어 더 안정됩니다. 높은 각도를 사용하면 더 많이 도달할 수 있습니다."
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance label"
msgid "Tree Support Branch Distance"
-msgstr ""
+msgstr "트리 지지물 브랜치 거리"
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance description"
msgid "How far apart the branches need to be when they touch the model. Making this distance small will cause the tree support to touch the model at more points, causing better overhang but making support harder to remove."
-msgstr ""
+msgstr "모델에 접촉할 때 브랜치를 떨어뜨려야 하는 정도 이 거리를 짧게 하면 트리 지지물이 더 많은 접점에서 모델에 접촉하여, 오버행이 더 좋아지지만 지지물을 제거하기가 더 어렵게 됩니다."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter label"
msgid "Tree Support Branch Diameter"
-msgstr ""
+msgstr "트리 지지물 브랜치 직경"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter description"
msgid "The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this."
-msgstr ""
+msgstr "트리 지지물의 가장 얇은 브랜치의 직경 브랜치가 두꺼울수록 더 견고해집니다. 베이스를 향한 브랜치는 이보다 더 두꺼워집니다."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle label"
msgid "Tree Support Branch Diameter Angle"
-msgstr ""
+msgstr "트리 지지물 브랜치 직경 각도"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle description"
msgid "The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the tree support."
-msgstr ""
+msgstr "하단을 향할수록 점점 더 두꺼워짐에 따른 브랜치의 직경 각도. 이 각도가 0이면 브랜치는 길이 전체에 균일한 두께를 갖게 됩니다. 약간의 각도가 있으면 트리 지지물의 안정성을 높여 줍니다."
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution label"
msgid "Tree Support Collision Resolution"
-msgstr ""
+msgstr "트리 지지물 충돌 해결"
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution description"
msgid "Resolution to compute collisions with to avoid hitting the model. Setting this lower will produce more accurate trees that fail less often, but increases slicing time dramatically."
-msgstr ""
+msgstr "모델에 부딪히는 것을 피하기 위해 충돌을 계산하는 해결책 이 값을 낮게 설정하면 실패도가 낮은 더 정확한 트리를 생성하지만, 슬라이싱 시간이 현격하게 늘어납니다."
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness label"
msgid "Tree Support Wall Thickness"
-msgstr ""
+msgstr "트리 지지물 벽 두께"
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness description"
msgid "The thickness of the walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "트리 지지물의 브랜치 벽의 두께 벽이 두꺼울수록 인쇄 시간이 오래 걸리지만 쉽게 넘어지지 않습니다."
#: fdmprinter.def.json
msgctxt "support_tree_wall_count label"
msgid "Tree Support Wall Line Count"
-msgstr ""
+msgstr "트리 지지물 벽 라인 카운트"
#: fdmprinter.def.json
msgctxt "support_tree_wall_count description"
msgid "The number of walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "트리 지지물의 브랜치 벽의 개수 벽이 두꺼울수록 인쇄 시간이 오래 걸리지만 쉽게 넘어지지 않습니다."
#: fdmprinter.def.json
msgctxt "slicing_tolerance label"
@@ -4414,12 +4412,12 @@ msgstr "상단 표면 스킨 층이 선 또는 지그재그 패턴을 사용할
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization label"
msgid "Infill Travel Optimization"
-msgstr ""
+msgstr "충진재 이동 최적화"
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization description"
msgid "When enabled, the order in which the infill lines are printed is optimized to reduce the distance travelled. The reduction in travel time achieved very much depends on the model being sliced, infill pattern, density, etc. Note that, for some models that have many small areas of infill, the time to slice the model may be greatly increased."
-msgstr ""
+msgstr "가능하면, 충진 라인 인쇄 순서가 최적화되어 이동 거리를 줄입니다. 이동 시간의 감소는 슬라이스되는 모델, 충진 패턴, 밀도 등에 따라 달라집니다. 작은 충진 영역이 많은 일부 모델의 경우, 모델을 슬라이스하는 시간이 상당히 늘어납니다."
#: fdmprinter.def.json
msgctxt "material_flow_dependent_temperature label"
@@ -5051,42 +5049,42 @@ msgstr "노즐과 수평 아래쪽 라인 사이의 거리. 클리어런스가
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled label"
msgid "Use adaptive layers"
-msgstr ""
+msgstr "어댑티브 레이어 사용"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled description"
msgid "Adaptive layers computes the layer heights depending on the shape of the model."
-msgstr ""
+msgstr "어댑티브 레이어는 모델의 모양에 따라 레이어의 높이를 계산합니다."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation label"
msgid "Adaptive layers maximum variation"
-msgstr ""
+msgstr "어댑티브 레이어 최대 변화"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation description"
msgid "The maximum allowed height different from the base layer height in mm."
-msgstr ""
+msgstr "기본 레이어 높이와 다른 최대 허용 높이 (mm)."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step label"
msgid "Adaptive layers variation step size"
-msgstr ""
+msgstr "어댑티브 레이어 변화 단계 크기"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step description"
msgid "The difference in height of the next layer height compared to the previous one."
-msgstr ""
+msgstr "이전 높이와 비교되는 다음 레이어 높이의 차이."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold label"
msgid "Adaptive layers threshold"
-msgstr ""
+msgstr "어댑티브 레이어 임계 값"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold description"
msgid "Threshold whether to use a smaller layer or not. This number is compared to the tan of the steepest slope in a layer."
-msgstr ""
+msgstr "더 작은 레이어를 사용할지 여부에 대한 임계 값 이 숫자는 레이어의 가장 급한 경사의 탄젠트와 비교됩니다."
#: fdmprinter.def.json
msgctxt "command_line_settings label"
diff --git a/resources/i18n/nl_NL/cura.po b/resources/i18n/nl_NL/cura.po
index 0c2d0236bc..48fe968815 100644
--- a/resources/i18n/nl_NL/cura.po
+++ b/resources/i18n/nl_NL/cura.po
@@ -1,14 +1,14 @@
# Cura
-# Copyright (C) 2017 Ultimaker
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
-"PO-Revision-Date: 2017-11-30 13:05+0100\n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
+"PO-Revision-Date: 2018-02-05 13:25+0100\n"
"Last-Translator: Bothof \n"
"Language-Team: Dutch\n"
"Language: nl_NL\n"
@@ -187,7 +187,7 @@ msgstr "Firmware van uw printer"
#: /home/ruben/Projects/Cura/plugins/PrepareStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Prepare"
-msgstr ""
+msgstr "Voorbereiden"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:23
msgctxt "@action:button Preceded by 'Ready to'."
@@ -560,7 +560,7 @@ msgstr "Opent de printtaken-interface in uw browser."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:239
msgctxt "@label Printer name"
msgid "Unknown"
-msgstr ""
+msgstr "Onbekend"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:505
#, python-brace-format
@@ -595,7 +595,7 @@ msgstr "Verbinding Maken via Netwerk"
#: /home/ruben/Projects/Cura/plugins/MonitorStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Monitor"
-msgstr ""
+msgstr "Controleren"
#: /home/ruben/Projects/Cura/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py:66
#, python-brace-format
@@ -622,7 +622,7 @@ msgstr "Geen toegang tot update-informatie."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:579
msgctxt "@info:status"
msgid "SolidWorks reported errors, while opening your file. We recommend to solve these issues inside SolidWorks itself."
-msgstr ""
+msgstr "SolidWorks heeft fouten gerapporteerd tijdens het openen van uw bestand. Het wordt aanbevolen deze problemen binnen SolidWorks zelf op te lossen."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:591
msgctxt "@info:status"
@@ -630,7 +630,7 @@ msgid ""
"Found no models inside your drawing. Could you please check it's content again and make sure one part or assembly is inside?\n"
"\n"
" Thanks!."
-msgstr ""
+msgstr "In uw tekening zijn geen modellen gevonden. Controleer de inhoud en zorg ervoor dat zich in de tekening een onderdeel of assemblage bevindt.\n\n Hartelijk dank."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:595
msgctxt "@info:status"
@@ -638,7 +638,7 @@ msgid ""
"Found more then one part or assembly inside your drawing. We currently only support drawings with exactly one part or assembly inside.\n"
"\n"
"Sorry!"
-msgstr ""
+msgstr "In uw tekening is meer dan één onderdeel of assemblage gevonden. Momenteel worden alleen tekeningen met precies één onderdeel of assemblage ondersteund.\n\nSorry."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:25
msgctxt "@item:inlistbox"
@@ -653,7 +653,7 @@ msgstr "Montagebestand SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:33
msgctxt "@item:inlistbox"
msgid "SolidWorks drawing file"
-msgstr ""
+msgstr "Tekenbestand SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:48
msgctxt "@info:status"
@@ -663,7 +663,7 @@ msgid ""
"\n"
"With kind regards\n"
" - Thomas Karl Pietrowski"
-msgstr ""
+msgstr "Beste klant,\nOp uw systeem is geen geldige installatie van SolidWorks aangetroffen. Dit betekent dat SolidWorks niet is geïnstalleerd of dat u niet over een geldige licentie beschikt. Controleer of SolidWorks zelf zonder problemen kan worden uitgevoerd en/of neem contact op met uw IT-afdeling.\n\nMet vriendelijke groeten\n - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:57
msgctxt "@info:status"
@@ -673,7 +673,7 @@ msgid ""
"\n"
"With kind regards\n"
" - Thomas Karl Pietrowski"
-msgstr ""
+msgstr "Beste klant,\nMomenteel voert u deze invoegtoepassing uit op een ander besturingssysteem dan Windows. Deze invoegtoepassing werkt alleen op systemen waarop Windows en SolidWorks met een geldige licentie zijn geïnstalleerd. Installeer deze invoegtoepassing op een Windows-systeem waarop SolidWorks is geïnstalleerd.\n\nMet vriendelijke groeten\n - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:70
msgid "Configure"
@@ -681,7 +681,7 @@ msgstr "Configureren"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:71
msgid "Installation guide for SolidWorks macro"
-msgstr ""
+msgstr "Installatiegids voor SolidWorks-macro"
#: /home/ruben/Projects/Cura/plugins/SimulationView/__init__.py:14
msgctxt "@item:inlistbox"
@@ -705,7 +705,7 @@ msgstr "G-code wijzigen"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:43
msgctxt "@info"
msgid "Cura collects anonymized usage statistics."
-msgstr ""
+msgstr "Cura verzamelt geanonimiseerde gebruiksstatistieken."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:46
msgctxt "@info:title"
@@ -715,22 +715,22 @@ msgstr "Gegevens verzamelen"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:48
msgctxt "@action:button"
msgid "Allow"
-msgstr ""
+msgstr "Toestaan"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:49
msgctxt "@action:tooltip"
msgid "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing."
-msgstr ""
+msgstr "Cura toestaan geanonimiseerde gebruiksstatistieken te verzenden om toekomstige verbeteringen aan Cura te helpen prioriteren. Onder de verzonden gegevens bevindt zich informatie over uw voorkeuren en instellingen, de Cura-versie en een selectie van de modellen die u slicet."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:50
msgctxt "@action:button"
msgid "Disable"
-msgstr ""
+msgstr "Uitschakelen"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:51
msgctxt "@action:tooltip"
msgid "Don't allow Cura to send anonymized usage statistics. You can enable it again in the preferences."
-msgstr ""
+msgstr "Cura niet toestaan geanonimiseerde gebruiksgegevens te verzenden. U kunt deze optie op een later tijdstip weer inschakelen in de voorkeuren."
#: /home/ruben/Projects/Cura/plugins/LegacyProfileReader/__init__.py:14
msgctxt "@item:inlistbox"
@@ -740,14 +740,14 @@ msgstr "Cura 15.04-profielen"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/__init__.py:15
msgctxt "@item:inlistbox"
msgid "Blender file"
-msgstr ""
+msgstr "Blender-bestand"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/CadIntegrationUtils/CommonReader.py:199
msgctxt "@info:status"
msgid ""
"Could not export using \"{}\" quality!\n"
"Felt back to \"{}\"."
-msgstr ""
+msgstr "Kan niet exporteren met de kwaliteit \"{}\"!\nInstelling teruggezet naar \"{}\"."
#: /home/ruben/Projects/Cura/plugins/GCodeProfileReader/__init__.py:14
#: /home/ruben/Projects/Cura/plugins/GCodeReader/__init__.py:14
@@ -934,12 +934,12 @@ msgstr "Cura-profiel"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:12
msgctxt "@item:inmenu"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Profielassistent"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:17
msgctxt "@item:inlistbox"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Profielassistent"
#: /home/ruben/Projects/Cura/plugins/3MFWriter/__init__.py:30
msgctxt "@item:inlistbox"
@@ -1137,13 +1137,13 @@ msgstr "Kan het profiel niet importeren uit {0}: {
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "This profile {0} contains incorrect data, could not import it."
-msgstr ""
+msgstr "Dit profiel {0} bevat incorrecte gegevens. Kan het profiel niet importeren."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:240
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "The machine defined in profile {0} doesn't match with your current machine, could not import it."
-msgstr ""
+msgstr "De machine die is vastgelegd in het profiel {0} komt niet overeen met uw huidige machine. Kan het profiel niet importeren."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:274
#, python-brace-format
@@ -1155,7 +1155,7 @@ msgstr "Het profiel {0} is geïmporteerd"
#, python-brace-format
msgctxt "@info:status"
msgid "File {0} does not contain any valid profile."
-msgstr ""
+msgstr "Het bestand {0} bevat geen geldig profiel."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:280
#, python-brace-format
@@ -1183,7 +1183,7 @@ msgstr "Kan geen kwaliteitstype {0} vinden voor de huidige configuratie."
#, python-brace-format
msgctxt "@label"
msgid "Group #{group_nr}"
-msgstr ""
+msgstr "Groepsnummer {group_nr}"
#: /home/ruben/Projects/Cura/cura/BuildVolume.py:100
msgctxt "@info:status"
@@ -1242,7 +1242,7 @@ msgid ""
"
A fatal error has occurred. Please send us this Crash Report to fix the problem
\n"
"
Please use the \"Send report\" button to post a bug report automatically to our servers
\n"
" "
-msgstr ""
+msgstr "
Er is een fatale fout opgetreden. Stuur ons het Crashrapport om het probleem op te lossen
\n
Druk op de knop \"Rapport verzenden\" om het foutenrapport automatisch naar onze servers te verzenden
"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:147
msgctxt "@title:groupbox"
msgid "Error traceback"
-msgstr ""
+msgstr "Traceback van fout"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:214
msgctxt "@title:groupbox"
@@ -1516,7 +1516,7 @@ msgstr "Maat nozzle"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:393
msgctxt "@label"
msgid "Compatible material diameter"
-msgstr ""
+msgstr "Compatibele materiaaldiameter"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:395
msgctxt "@tooltip"
@@ -1618,10 +1618,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 ""
-"Als u rechtstreeks via het netwerk wilt printen naar de printer, moet u ervoor zorgen dat de printer met een netwerkkabel is verbonden met het netwerk of moet u verbinding maken met de printer via het wifi-netwerk. Als u geen verbinding maakt tussen Cura en de printer, kunt u een USB-station gebruiken om g-code-bestanden naar de printer over te zetten.\n"
-"\n"
-"Selecteer uw printer in de onderstaande lijst:"
+msgstr "Als u rechtstreeks via het netwerk wilt printen naar de printer, moet u ervoor zorgen dat de printer met een netwerkkabel is verbonden met het netwerk of moet u verbinding maken met de printer via het wifi-netwerk. Als u geen verbinding maakt tussen Cura en de printer, kunt u een USB-station gebruiken om g-code-bestanden naar de printer over te zetten.\n\nSelecteer uw printer in de onderstaande lijst:"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:75
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:44
@@ -1661,12 +1658,12 @@ msgstr "Type"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:233
msgctxt "@label Printer name"
msgid "Ultimaker 3"
-msgstr ""
+msgstr "Ultimaker 3"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:236
msgctxt "@label Printer name"
msgid "Ultimaker 3 Extended"
-msgstr ""
+msgstr "Ultimaker 3 Extended"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:252
msgctxt "@label"
@@ -1733,7 +1730,7 @@ msgstr "%1 is niet ingesteld voor het hosten van een groep aangesloten Ultimaker
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml:55
msgctxt "@label link to connect manager"
msgid "Add/Remove printers"
-msgstr ""
+msgstr "Printers toevoegen/verwijderen"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/OpenPanelButton.qml:14
msgctxt "@info:tooltip"
@@ -1773,7 +1770,7 @@ msgstr "Verbinding met de printer is verbroken"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:47
msgctxt "@label Printer status"
msgid "Unknown"
-msgstr ""
+msgstr "Onbekend"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:257
msgctxt "@label:status"
@@ -1869,164 +1866,164 @@ msgstr "Configuratie Activeren"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:21
msgctxt "@title:window"
msgid "SolidWorks: Export wizard"
-msgstr ""
+msgstr "SolidWorks: exportwizard"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:45
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:140
msgctxt "@action:label"
msgid "Quality:"
-msgstr ""
+msgstr "Kwaliteit:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:78
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:179
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (3D-printing)"
-msgstr ""
+msgstr "Fijn (3D-printen)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:79
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:180
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (3D-printing)"
-msgstr ""
+msgstr "Grof (3D-printen)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:80
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:181
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (SolidWorks)"
-msgstr ""
+msgstr "Fijn (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:81
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:182
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (SolidWorks)"
-msgstr ""
+msgstr "Grof (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:94
msgctxt "@text:window"
msgid "Show this dialog again"
-msgstr ""
+msgstr "Dit dialoogvenster opnieuw tonen"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:104
msgctxt "@action:button"
msgid "Continue"
-msgstr ""
+msgstr "Verder"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:116
msgctxt "@action:button"
msgid "Abort"
-msgstr ""
+msgstr "Afbreken"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:21
msgctxt "@title:window"
msgid "How to install Cura SolidWorks macro"
-msgstr ""
+msgstr "De Cura SolidWorks-macro installeren"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:62
msgctxt "@description:label"
msgid "Steps:"
-msgstr ""
+msgstr "Stappen:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:140
msgctxt "@action:button"
msgid ""
"Open the directory\n"
"with macro and icon"
-msgstr ""
+msgstr "Open de map\nmet macro en pictogram"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:160
msgctxt "@description:label"
msgid "Instructions:"
-msgstr ""
+msgstr "Instructies:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:202
msgctxt "@action:playpause"
msgid "Play"
-msgstr ""
+msgstr "Afspelen"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:206
msgctxt "@action:playpause"
msgid "Pause"
-msgstr ""
+msgstr "Pauzeren"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:268
msgctxt "@action:button"
msgid "Previous Step"
-msgstr ""
+msgstr "Vorige stap"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:283
msgctxt "@action:button"
msgid "Done"
-msgstr ""
+msgstr "Gereed"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:287
msgctxt "@action:button"
msgid "Next Step"
-msgstr ""
+msgstr "Volgende stap"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:21
msgctxt "@title:window"
msgid "SolidWorks plugin: Configuration"
-msgstr ""
+msgstr "SolidWorks-invoegtoepassing: configuratie"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:39
msgctxt "@title:tab"
msgid "Conversion settings"
-msgstr ""
+msgstr "Conversie-instellingen"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:66
msgctxt "@label"
msgid "First choice:"
-msgstr ""
+msgstr "Eerste keuze:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:86
msgctxt "@text:menu"
msgid "Latest installed version (Recommended)"
-msgstr ""
+msgstr "Recentst geïnstalleerde versie (aanbevolen)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:95
msgctxt "@text:menu"
msgid "Default version"
-msgstr ""
+msgstr "Standaardversie"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:193
msgctxt "@label"
msgid "Show wizard before opening SolidWorks files"
-msgstr ""
+msgstr "Wizard tonen voordat SolidWorks-bestanden worden geopend"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:203
msgctxt "@label"
msgid "Automatically rotate opened file into normed orientation"
-msgstr ""
+msgstr "Geopend bestand automatisch roteren naar de genormaliseerde stand"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:210
msgctxt "@title:tab"
msgid "Installation(s)"
-msgstr ""
+msgstr "Installatie(s)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:284
msgctxt "@label"
msgid "COM service found"
-msgstr ""
+msgstr "COM-service gevonden"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:295
msgctxt "@label"
msgid "Executable found"
-msgstr ""
+msgstr "Uitvoerbaar bestand gevonden"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:306
msgctxt "@label"
msgid "COM starting"
-msgstr ""
+msgstr "COM starten"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:317
msgctxt "@label"
msgid "Revision number"
-msgstr ""
+msgstr "Revisienummer"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:328
msgctxt "@label"
msgid "Functions available"
-msgstr ""
+msgstr "Beschikbare functies"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:341
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:264
@@ -2212,32 +2209,32 @@ msgstr "Effenen"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:38
msgctxt "@label"
msgid "Mesh Type"
-msgstr ""
+msgstr "Rastertype"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:69
msgctxt "@label"
msgid "Normal model"
-msgstr ""
+msgstr "Normaal model"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:76
msgctxt "@label"
msgid "Print as support"
-msgstr ""
+msgstr "Printen als supportstructuur"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:84
msgctxt "@label"
msgid "Don't support overlap with other models"
-msgstr ""
+msgstr "Supportstructuur niet laten overlappen met andere modellen"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:92
msgctxt "@label"
msgid "Modify settings for overlap with other models"
-msgstr ""
+msgstr "Instellingen aanpassen voor overlapping met andere modellen"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:100
msgctxt "@label"
msgid "Modify settings for infill of other models"
-msgstr ""
+msgstr "Instellingen aanpassen voor vulling van andere modellen"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:333
msgctxt "@action:button"
@@ -2423,10 +2420,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 ""
-"Deze invoegtoepassing bevat een licentie.\n"
-"U moet akkoord gaan met deze licentie om deze invoegtoepassing te mogen installeren.\n"
-"Gaat u akkoord met de onderstaande voorwaarden?"
+msgstr "Deze invoegtoepassing bevat een licentie.\nU moet akkoord gaan met deze licentie om deze invoegtoepassing te mogen installeren.\nGaat u akkoord met de onderstaande voorwaarden?"
#: /home/ruben/Projects/Cura/plugins/PluginBrowser/PluginBrowser.qml:242
msgctxt "@action:button"
@@ -2691,9 +2685,7 @@ msgctxt "@text:window"
msgid ""
"You have customized some profile settings.\n"
"Would you like to keep or discard those settings?"
-msgstr ""
-"U hebt enkele profielinstellingen aangepast.\n"
-"Wilt u deze instellingen behouden of verwijderen?"
+msgstr "U hebt enkele profielinstellingen aangepast.\nWilt u deze instellingen behouden of verwijderen?"
#: /home/ruben/Projects/Cura/resources/qml/DiscardOrKeepProfileChangesDialog.qml:110
msgctxt "@title:column"
@@ -3097,27 +3089,27 @@ msgstr "(Anonieme) printgegevens verzenden"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:674
msgctxt "@label"
msgid "Experimental"
-msgstr ""
+msgstr "Experimenteel"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:680
msgctxt "@info:tooltip"
msgid "Use multi build plate functionality"
-msgstr ""
+msgstr "Functionaliteit voor meerdere platformen gebruiken"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:685
msgctxt "@option:check"
msgid "Use multi build plate functionality (restart required)"
-msgstr ""
+msgstr "Functionaliteit voor meerdere platformen gebruiken (opnieuw opstarten vereist)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:694
msgctxt "@info:tooltip"
msgid "Should newly loaded models be arranged on the build plate? Used in conjunction with multi build plate (EXPERIMENTAL)"
-msgstr ""
+msgstr "Moeten nieuw geladen modellen op het platform worden geschikt? Gebruikt in combinatie met meerdere platformen (EXPERIMENTEEL)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:699
msgctxt "@option:check"
msgid "Do not arrange objects on load"
-msgstr ""
+msgstr "Objecten niet schikken na laden"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:15
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:514
@@ -3361,9 +3353,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 is ontwikkeld door Ultimaker B.V. in samenwerking met de community.\n"
-"Cura maakt met trots gebruik van de volgende opensourceprojecten:"
+msgstr "Cura is ontwikkeld door Ultimaker B.V. in samenwerking met de community.\nCura maakt met trots gebruik van de volgende opensourceprojecten:"
#: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:118
msgctxt "@label"
@@ -3471,10 +3461,7 @@ msgid ""
"Some setting/override values are different from the values stored in the profile.\n"
"\n"
"Click to open the profile manager."
-msgstr ""
-"Sommige waarden of aanpassingen van instellingen zijn anders dan de waarden die in het profiel zijn opgeslagen.\n"
-"\n"
-"Klik om het profielbeheer te openen."
+msgstr "Sommige waarden of aanpassingen van instellingen zijn anders dan de waarden die in het profiel zijn opgeslagen.\n\nKlik om het profielbeheer te openen."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:150
msgctxt "@label:textbox"
@@ -3512,10 +3499,7 @@ msgid ""
"Some hidden settings use values different from their normal calculated value.\n"
"\n"
"Click to make these settings visible."
-msgstr ""
-"Een aantal verborgen instellingen gebruiken andere waarden dan hun normale berekende waarde.\n"
-"\n"
-"Klik om deze instellingen zichtbaar te maken."
+msgstr "Een aantal verborgen instellingen gebruiken andere waarden dan hun normale berekende waarde.\n\nKlik om deze instellingen zichtbaar te maken."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:61
msgctxt "@label Header for list of settings."
@@ -3530,7 +3514,7 @@ msgstr "Beïnvloed door"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:156
msgctxt "@label"
msgid "This setting is always shared between all extruders. Changing it here will change the value for all extruders."
-msgstr ""
+msgstr "Deze instelling wordt altijd door alle extruders gedeeld. Als u hier de instelling wijzigt, wordt de waarde voor alle extruders gewijzigd"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:159
msgctxt "@label"
@@ -3543,10 +3527,7 @@ msgid ""
"This setting has a value that is different from the profile.\n"
"\n"
"Click to restore the value of the profile."
-msgstr ""
-"Deze instelling heeft een andere waarde dan in het profiel.\n"
-"\n"
-"Klik om de waarde van het profiel te herstellen."
+msgstr "Deze instelling heeft een andere waarde dan in het profiel.\n\nKlik om de waarde van het profiel te herstellen."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:288
msgctxt "@label"
@@ -3554,10 +3535,7 @@ msgid ""
"This setting is normally calculated, but it currently has an absolute value set.\n"
"\n"
"Click to restore the calculated value."
-msgstr ""
-"Deze instelling wordt normaliter berekend, maar is nu ingesteld op een absolute waarde.\n"
-"\n"
-"Klik om de berekende waarde te herstellen."
+msgstr "Deze instelling wordt normaliter berekend, maar is nu ingesteld op een absolute waarde.\n\nKlik om de berekende waarde te herstellen."
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:128
msgctxt "@label:listbox"
@@ -3569,9 +3547,7 @@ msgctxt "@label:listbox"
msgid ""
"Print Setup disabled\n"
"G-code files cannot be modified"
-msgstr ""
-"Instelling voor printen uitgeschakeld\n"
-"G-code-bestanden kunnen niet worden aangepast"
+msgstr "Instelling voor printen uitgeschakeld\nG-code-bestanden kunnen niet worden aangepast"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:342
msgctxt "@label Hours and minutes"
@@ -3581,7 +3557,7 @@ msgstr "00u 00min"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:359
msgctxt "@tooltip"
msgid "Time specification"
-msgstr ""
+msgstr "Tijdspecificatie"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:441
msgctxt "@label"
@@ -3638,12 +3614,12 @@ msgstr "Beel&d"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:37
msgctxt "@action:inmenu menubar:view"
msgid "&Camera position"
-msgstr ""
+msgstr "&Camerapositie"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:52
msgctxt "@action:inmenu menubar:view"
msgid "&Build plate"
-msgstr ""
+msgstr "&Platform"
#: /home/ruben/Projects/Cura/resources/qml/Menus/NozzleMenu.qml:40
msgctxt "@title:menuitem %1 is the nozzle currently loaded in the printer"
@@ -3813,27 +3789,27 @@ msgstr "&Afsluiten"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:114
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
-msgstr ""
+msgstr "&3D-weergave"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:121
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
-msgstr ""
+msgstr "Weergave &voorzijde"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:128
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
-msgstr ""
+msgstr "Weergave &bovenzijde"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:135
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
-msgstr ""
+msgstr "Weergave &linkerzijde"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:142
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
-msgstr ""
+msgstr "Weergave &rechterzijde"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:149
msgctxt "@action:inmenu"
@@ -3959,7 +3935,7 @@ msgstr "Alle Modellen Opnieuw &Laden"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:edit"
msgid "Arrange All Models To All Build Plates"
-msgstr ""
+msgstr "Alle modellen schikken op alle platformen"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:358
msgctxt "@action:inmenu menubar:edit"
@@ -4019,7 +3995,7 @@ msgstr "Geïnstalleerde plugins..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:438
msgctxt "@action:inmenu menubar:view"
msgid "Expand/Collapse Sidebar"
-msgstr ""
+msgstr "Zijbalk uitbreiden/samenvouwen"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:26
msgctxt "@label:PrintjobStatus"
@@ -4054,12 +4030,12 @@ msgstr "Slicen is niet beschikbaar"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Slice current printjob"
-msgstr ""
+msgstr "Huidige printtaak slicen"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Cancel slicing process"
-msgstr ""
+msgstr "Slicen annuleren"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:183
msgctxt "@label:Printjob"
@@ -4115,7 +4091,7 @@ msgstr "Opslaan &als..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:139
msgctxt "@title:menu menubar:file"
msgid "Save &Project..."
-msgstr ""
+msgstr "&Project opslaan..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:162
msgctxt "@title:menu menubar:toplevel"
@@ -4354,7 +4330,7 @@ msgstr "Materiaal"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:352
msgctxt "@label"
msgid "Check compatibility"
-msgstr ""
+msgstr "Compatibiliteit controleren"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:372
msgctxt "@tooltip"
@@ -4364,17 +4340,17 @@ msgstr "Klik om de materiaalcompatibiliteit te controleren op Ultimaker.com."
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:211
msgctxt "@option:check"
msgid "See only current build plate"
-msgstr ""
+msgstr "Alleen huidig platform weergeven"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:227
msgctxt "@action:button"
msgid "Arrange to all build plates"
-msgstr ""
+msgstr "Schikken naar alle platformen"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:247
msgctxt "@action:button"
msgid "Arrange current build plate"
-msgstr ""
+msgstr "Huidig platform schikken"
#: MachineSettingsAction/plugin.json
msgctxt "description"
@@ -4469,22 +4445,22 @@ msgstr "USB-printen"
#: PrepareStage/plugin.json
msgctxt "description"
msgid "Provides a prepare stage in Cura."
-msgstr ""
+msgstr "Deze optie biedt een voorbereidingsstadium in Cura."
#: PrepareStage/plugin.json
msgctxt "name"
msgid "Prepare Stage"
-msgstr ""
+msgstr "Stadium voorbereiden"
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "description"
msgid "Provides an edit window for direct script editing."
-msgstr ""
+msgstr "Deze optie biedt een bewerkingsvenster voor rechtstreeks bewerken van scripts."
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "name"
msgid "Live scripting tool"
-msgstr ""
+msgstr "Gereedschap voor live uitvoeren van scripts"
#: RemovableDriveOutputDevice/plugin.json
msgctxt "description"
@@ -4509,12 +4485,12 @@ msgstr "UM3-netwerkverbinding"
#: MonitorStage/plugin.json
msgctxt "description"
msgid "Provides a monitor stage in Cura."
-msgstr ""
+msgstr "Deze optie biedt een controlestadium in Cura."
#: MonitorStage/plugin.json
msgctxt "name"
msgid "Monitor Stage"
-msgstr ""
+msgstr "Controlestadium"
#: FirmwareUpdateChecker/plugin.json
msgctxt "description"
@@ -4529,7 +4505,7 @@ msgstr "Firmware-updatecontrole"
#: CuraSolidWorksPlugin/plugin.json
msgctxt "description"
msgid "Gives you the possibility to open certain files using SolidWorks itself. Conversion is done by this plugin and additional optimizations."
-msgstr ""
+msgstr "Deze optie biedt u de mogelijkheid bepaalde bestanden via SolidWorks te openen. De bestanden worden vervolgens via deze invoegtoepassing geconverteerd en geoptimaliseerd."
#: CuraSolidWorksPlugin/plugin.json
msgctxt "name"
@@ -4599,12 +4575,12 @@ msgstr "Lezer voor Profielen van oudere Cura-versies"
#: CuraBlenderPlugin/plugin.json
msgctxt "description"
msgid "Helps to open Blender files directly in Cura."
-msgstr ""
+msgstr "Hiermee kunnen Blender-bestanden rechtstreeks in Cura worden geopend."
#: CuraBlenderPlugin/plugin.json
msgctxt "name"
msgid "Blender Integration (experimental)"
-msgstr ""
+msgstr "Blender-integratie (experimenteel)"
#: GCodeProfileReader/plugin.json
msgctxt "description"
@@ -4769,12 +4745,12 @@ msgstr "Cura-profielschrijver"
#: CuraPrintProfileCreator/plugin.json
msgctxt "description"
msgid "Allows material manufacturers to create new material and quality profiles using a drop-in UI."
-msgstr ""
+msgstr "Maakt het fabrikanten mogelijk nieuwe materiaal- en kwaliteitsprofielen aan te maken met behulp van een drop-in-gebruikersinterface."
#: CuraPrintProfileCreator/plugin.json
msgctxt "name"
msgid "Print Profile Assistant"
-msgstr ""
+msgstr "Profielassistent afdrukken"
#: 3MFWriter/plugin.json
msgctxt "description"
diff --git a/resources/i18n/nl_NL/fdmprinter.def.json.po b/resources/i18n/nl_NL/fdmprinter.def.json.po
index eb846edf25..36f7ece010 100644
--- a/resources/i18n/nl_NL/fdmprinter.def.json.po
+++ b/resources/i18n/nl_NL/fdmprinter.def.json.po
@@ -348,12 +348,12 @@ msgstr "Repetier"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract label"
msgid "Firmware Retraction"
-msgstr ""
+msgstr "Intrekken via firmware"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract description"
msgid "Whether to use firmware retract commands (G10/G11) instead of using the E property in G1 commands to retract the material."
-msgstr ""
+msgstr "Hiermee bepaalt u of u voor het intrekken van materiaal firmwareopdrachten voor intrekken (G10/G11) gebruikt in plaats van de eigenschap E in G1-opdrachten."
#: fdmprinter.def.json
msgctxt "machine_disallowed_areas label"
@@ -1048,12 +1048,12 @@ msgstr "Overal"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps label"
msgid "Filter Out Tiny Gaps"
-msgstr ""
+msgstr "Kleine gaten wegfilteren"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps description"
msgid "Filter out tiny gaps to reduce blobs on outside of model."
-msgstr ""
+msgstr "Kleine gaten wegfilteren om blobs aan de buitenzijde van het model te verminderen."
#: fdmprinter.def.json
msgctxt "fill_outline_gaps label"
@@ -1438,7 +1438,7 @@ msgstr "Vulling X-offset"
#: fdmprinter.def.json
msgctxt "infill_offset_x description"
msgid "The infill pattern is moved this distance along the X axis."
-msgstr ""
+msgstr "Het vulpatroon wordt over deze afstand verplaatst langs de X-as."
#: fdmprinter.def.json
msgctxt "infill_offset_y label"
@@ -1448,7 +1448,7 @@ msgstr "Vulling Y-offset"
#: fdmprinter.def.json
msgctxt "infill_offset_y description"
msgid "The infill pattern is moved this distance along the Y axis."
-msgstr ""
+msgstr "Het vulpatroon wordt over deze afstand verplaatst langs de Y-as."
#: fdmprinter.def.json
msgctxt "sub_div_rad_add label"
@@ -1468,7 +1468,7 @@ msgstr "Overlappercentage vulling"
#: fdmprinter.def.json
msgctxt "infill_overlap description"
msgid "The amount of overlap between the infill and the walls as a percentage of the infill line width. A slight overlap allows the walls to connect firmly to the infill."
-msgstr ""
+msgstr "De mate van overlap tussen de vulling en de wanden als percentage van de lijnbreedte van de vulling. Met een lichte overlap kunnen de wanden goed hechten aan de vulling."
#: fdmprinter.def.json
msgctxt "infill_overlap_mm label"
@@ -1488,7 +1488,7 @@ msgstr "Overlappercentage Skin"
#: fdmprinter.def.json
msgctxt "skin_overlap description"
msgid "The amount of overlap between the skin and the walls as a percentage of the skin line width. A slight overlap allows the walls to connect firmly to the skin. This is a percentage of the average line widths of the skin lines and the innermost wall."
-msgstr ""
+msgstr "De mate van overlap tussen de skin en de wanden als percentage van de lijnbreedte van de skin. Met een lichte overlap kunnen de wanden goed hechten aan de skin. Dit is een percentage van de gemiddelde lijnbreedte van de skinlijnen en de binnenste wand."
#: fdmprinter.def.json
msgctxt "skin_overlap_mm label"
@@ -1718,7 +1718,7 @@ msgstr "Platformtemperatuur"
#: fdmprinter.def.json
msgctxt "material_bed_temperature description"
msgid "The temperature used for the heated build plate. If this is 0, the bed temperature will not be adjusted."
-msgstr ""
+msgstr "De temperatuur van het verwarmde platform. Als deze waarde is ingesteld op 0, wordt de printbedtemperatuur niet aangepast."
#: fdmprinter.def.json
msgctxt "material_bed_temperature_layer_0 label"
@@ -3500,9 +3500,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 ""
-"De horizontale afstand tussen de skirt en de eerste laag van de print.\n"
-"Dit is de minimumafstand. Als u meerdere skirtlijnen print, worden deze vanaf deze afstand naar buiten geprint."
+msgstr "De horizontale afstand tussen de skirt en de eerste laag van de print.\nDit is de minimumafstand. Als u meerdere skirtlijnen print, worden deze vanaf deze afstand naar buiten geprint."
#: fdmprinter.def.json
msgctxt "skirt_brim_minimal_length label"
@@ -4262,82 +4260,82 @@ msgstr "experimenteel!"
#: fdmprinter.def.json
msgctxt "support_tree_enable label"
msgid "Tree Support"
-msgstr ""
+msgstr "Boomsupportstructuur"
#: fdmprinter.def.json
msgctxt "support_tree_enable description"
msgid "Generate a tree-like support with branches that support your print. This may reduce material usage and print time, but greatly increases slicing time."
-msgstr ""
+msgstr "Genereer een boomachtige supportstructuur met takken die uw print ondersteunen. Hierdoor worden materiaalgebruik en printtijd mogelijk gereduceerd, maar wordt de slicetijd aanzienlijk verlengd."
#: fdmprinter.def.json
msgctxt "support_tree_angle label"
msgid "Tree Support Branch Angle"
-msgstr ""
+msgstr "Hoek van takken van boomsupportstructuur"
#: fdmprinter.def.json
msgctxt "support_tree_angle description"
msgid "The angle of the branches. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach."
-msgstr ""
+msgstr "Hiermee stelt u de hoek van de takken in. Met een kleinere hoek worden de takken verticaler en stabieler. Met een grotere hoek hebben ze een groter bereik."
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance label"
msgid "Tree Support Branch Distance"
-msgstr ""
+msgstr "Takafstand van boomsupportstructuur"
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance description"
msgid "How far apart the branches need to be when they touch the model. Making this distance small will cause the tree support to touch the model at more points, causing better overhang but making support harder to remove."
-msgstr ""
+msgstr "Hiermee stelt u in hoe ver de takken moeten uitsteken als ze het model raken. Met een kleinere afstand raakt de boomsupportstructuur het model op meer plaatsen. Hierdoor creëert u een betere overhang maar is de supportstructuur moeilijker te verwijderen."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter label"
msgid "Tree Support Branch Diameter"
-msgstr ""
+msgstr "Takdiameter van boomsupportstructuur"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter description"
msgid "The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this."
-msgstr ""
+msgstr "Hiermee stelt u de diameter in van de dunste takken van de boomsupportstructuur. Dikkere takken zijn steviger. Takken die dichter bij de stam liggen, zijn dikker dan dit."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle label"
msgid "Tree Support Branch Diameter Angle"
-msgstr ""
+msgstr "Hoek van takdiameter van boomsupportstructuur"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle description"
msgid "The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the tree support."
-msgstr ""
+msgstr "De hoek van de diameter van de takken terwijl ze naar beneden toe geleidelijk dikker worden. Met de hoekinstelling 0 zijn de takken over de gehele lengte even dik. Een kleine hoek verbetert de stabiliteit van de boomsupportstructuur."
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution label"
msgid "Tree Support Collision Resolution"
-msgstr ""
+msgstr "Resolutie bij botsingen van de boomsupportstructuur"
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution description"
msgid "Resolution to compute collisions with to avoid hitting the model. Setting this lower will produce more accurate trees that fail less often, but increases slicing time dramatically."
-msgstr ""
+msgstr "Resolutie voor het berekenen van botsingen om te voorkomen dat het model wordt geraakt. Als u deze optie op een lagere waarde instelt, creëert u nauwkeurigere bomen die minder vaak fouten vertonen, maar wordt de slicetijd aanzienlijk verlengd."
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness label"
msgid "Tree Support Wall Thickness"
-msgstr ""
+msgstr "Wanddikte boomsupportstructuur"
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness description"
msgid "The thickness of the walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "Hiermee stelt u de wanddikte in voor de takken van de boomsupportstructuur. Het printen van dikkere wanden duurt langer, maar deze vallen minder snel om."
#: fdmprinter.def.json
msgctxt "support_tree_wall_count label"
msgid "Tree Support Wall Line Count"
-msgstr ""
+msgstr "Aantal wandlijnen boomsupportstructuur"
#: fdmprinter.def.json
msgctxt "support_tree_wall_count description"
msgid "The number of walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "Hiermee stelt u het aantal wanden in voor de takken van de boomsupportstructuur. Het printen van dikkere wanden duurt langer, maar deze vallen minder snel om."
#: fdmprinter.def.json
msgctxt "slicing_tolerance label"
@@ -4412,12 +4410,12 @@ msgstr "Een lijst met gehele getallen voor lijnrichtingen die moet worden gebrui
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization label"
msgid "Infill Travel Optimization"
-msgstr ""
+msgstr "Bewegingsoptimalisatie vulling"
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization description"
msgid "When enabled, the order in which the infill lines are printed is optimized to reduce the distance travelled. The reduction in travel time achieved very much depends on the model being sliced, infill pattern, density, etc. Note that, for some models that have many small areas of infill, the time to slice the model may be greatly increased."
-msgstr ""
+msgstr "Wanneer deze optie is ingeschakeld, wordt de volgorde geoptimaliseerd waarin de vullijnen worden geprint om de afgelegde beweging te reduceren. De reductie in bewegingstijd die wordt bereikt, is in hoge mate afhankelijk van het model dat wordt geslicet, het vulpatroon, de dichtheid enz. Houd er rekening mee dat de slicetijd voor modellen met veel kleine vulgebieden aanzienlijk kan worden verlengd."
#: fdmprinter.def.json
msgctxt "material_flow_dependent_temperature label"
@@ -4939,9 +4937,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 ""
-"De afstand van een opwaartse beweging waarbij de doorvoersnelheid wordt gehalveerd.\n"
-"Hierdoor ontstaat een betere hechting aan voorgaande lagen, zonder dat het materiaal in die lagen te zeer wordt verwarmd. Alleen van toepassing op Draadprinten."
+msgstr "De afstand van een opwaartse beweging waarbij de doorvoersnelheid wordt gehalveerd.\nHierdoor ontstaat een betere hechting aan voorgaande lagen, zonder dat het materiaal in die lagen te zeer wordt verwarmd. Alleen van toepassing op Draadprinten."
#: fdmprinter.def.json
msgctxt "wireframe_top_jump label"
@@ -5051,42 +5047,42 @@ msgstr "De afstand tussen de nozzle en horizontaal neergaande lijnen. Een groter
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled label"
msgid "Use adaptive layers"
-msgstr ""
+msgstr "Adaptieve lagen gebruiken"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled description"
msgid "Adaptive layers computes the layer heights depending on the shape of the model."
-msgstr ""
+msgstr "Met adaptieve lagen berekent u de laaghoogte afhankelijk van de vorm van het model."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation label"
msgid "Adaptive layers maximum variation"
-msgstr ""
+msgstr "Maximale variatie adaptieve lagen"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation description"
msgid "The maximum allowed height different from the base layer height in mm."
-msgstr ""
+msgstr "De maximaal toegestane hoogte ten opzichte van de grondlaaghoogte in mm."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step label"
msgid "Adaptive layers variation step size"
-msgstr ""
+msgstr "Stapgrootte variatie adaptieve lagen"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step description"
msgid "The difference in height of the next layer height compared to the previous one."
-msgstr ""
+msgstr "Het hoogteverschil tussen de hoogte van de volgende laag ten opzichte van de vorige laag."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold label"
msgid "Adaptive layers threshold"
-msgstr ""
+msgstr "Drempel adaptieve lagen"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold description"
msgid "Threshold whether to use a smaller layer or not. This number is compared to the tan of the steepest slope in a layer."
-msgstr ""
+msgstr "De drempel of er al dan niet een kleinere laag moet worden gebruikt. Deze waarde wordt vergeleken met de waarde van de steilste helling in een laag."
#: fdmprinter.def.json
msgctxt "command_line_settings label"
diff --git a/resources/i18n/pl_PL/cura.po b/resources/i18n/pl_PL/cura.po
index 7cf338b1e7..c612eefc13 100644
--- a/resources/i18n/pl_PL/cura.po
+++ b/resources/i18n/pl_PL/cura.po
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: Cura 3.0\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
"POT-Creation-Date: 2017-08-02 16:53+0000\n"
-"PO-Revision-Date: 2017-11-22 16:19+0100\n"
+"PO-Revision-Date: 2018-02-10 14:24+0100\n"
"Last-Translator: 'Jaguś' Paweł Jagusiak and Andrzej 'anraf1001' Rafalski\n"
"Language-Team: reprapy.pl\n"
"Language: pl_PL\n"
@@ -16,7 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Poedit 2.0.4\n"
+"X-Generator: Poedit 2.0.6\n"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.py:26
msgctxt "@action"
@@ -189,7 +189,7 @@ msgstr "Oprogramowanie Drukarki"
#: /home/ruben/Projects/Cura/plugins/PrepareStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Prepare"
-msgstr ""
+msgstr "Przygotuj"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:23
msgctxt "@action:button Preceded by 'Ready to'."
@@ -562,7 +562,7 @@ msgstr "Otwiera interfejs zadań druku w przeglądarce."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:239
msgctxt "@label Printer name"
msgid "Unknown"
-msgstr ""
+msgstr "Nieznana"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:505
#, python-brace-format
@@ -597,7 +597,7 @@ msgstr "Połącz przez sieć"
#: /home/ruben/Projects/Cura/plugins/MonitorStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Monitor"
-msgstr ""
+msgstr "Monitor"
#: /home/ruben/Projects/Cura/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py:66
#, python-brace-format
@@ -624,7 +624,7 @@ msgstr "Nie można uzyskać dostępu do informacji o aktualizacji"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:579
msgctxt "@info:status"
msgid "SolidWorks reported errors, while opening your file. We recommend to solve these issues inside SolidWorks itself."
-msgstr ""
+msgstr "SolidWorks zgłosił błędy podczas otwierania twojego pliku. Zalecamy rozwiązanie tego problemu w samym SolidWorksie."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:591
msgctxt "@info:status"
@@ -633,6 +633,9 @@ msgid ""
"\n"
" Thanks!."
msgstr ""
+"Nie znaleziono modeli wewnątrz twojego rysunku. Czy mógłbyś sprawdzić jego zawartość ponownie i upewnić się, że znajduje się tam jedna część lub złożenie?\n"
+"\n"
+"Dziękuję!."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:595
msgctxt "@info:status"
@@ -641,6 +644,9 @@ msgid ""
"\n"
"Sorry!"
msgstr ""
+"Znaleziono więcej niż jedną część lub złożenie wewnątrz rysunku. Obecnie obsługujemy tylko rysunki z dokładnie jedną częścią lub złożeniem.\n"
+"\n"
+"Przepraszamy!"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:25
msgctxt "@item:inlistbox"
@@ -655,7 +661,7 @@ msgstr "Plik złożenia SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:33
msgctxt "@item:inlistbox"
msgid "SolidWorks drawing file"
-msgstr ""
+msgstr "Plik rysunku SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:48
msgctxt "@info:status"
@@ -666,6 +672,11 @@ msgid ""
"With kind regards\n"
" - Thomas Karl Pietrowski"
msgstr ""
+"Szanowny kliencie,\n"
+"Nie mogliśmy znaleźć poprawnej instalacji SolidWorks w twoim systemie. To oznacza, że albo nie masz zainstalowanego SolidWorks, albo nie masz ważnej licencji. Proszę upewnić się, że uruchomiony SolidWorks działa bez żadnych problemów i/lub skontaktuj się z ICT.\n"
+"\n"
+"Z wyrazami szacunku,\n"
+" - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:57
msgctxt "@info:status"
@@ -676,6 +687,10 @@ msgid ""
"With kind regards\n"
" - Thomas Karl Pietrowski"
msgstr ""
+"Szanowny kliencie,\n"
+"Używasz aktualnie tego pluginu na innym systemie niż Windows. Ten plugin działa tylko w Windows razem z zainstalowanym SolidWorks, włączając w to poprawną licencję. Proszę zainstalować ten plugin na maszynie z systemem Windows z zainstalowanym SolidWorks.\n"
+"Z wyrazami szacunku,\n"
+" - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:70
msgid "Configure"
@@ -683,7 +698,7 @@ msgstr "Konfiguruj"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:71
msgid "Installation guide for SolidWorks macro"
-msgstr ""
+msgstr "Instalacja poradnika dla skrótów SolidWorks"
#: /home/ruben/Projects/Cura/plugins/SimulationView/__init__.py:14
msgctxt "@item:inlistbox"
@@ -707,7 +722,7 @@ msgstr "Modyfikuj G-Code"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:43
msgctxt "@info"
msgid "Cura collects anonymized usage statistics."
-msgstr ""
+msgstr "Cura zbiera anonimowe dane statystyczne."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:46
msgctxt "@info:title"
@@ -717,22 +732,22 @@ msgstr "Zbieranie Danych"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:48
msgctxt "@action:button"
msgid "Allow"
-msgstr ""
+msgstr "Zezwól"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:49
msgctxt "@action:tooltip"
msgid "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing."
-msgstr ""
+msgstr "Zezwól Cura na wysyłanie anonimowych danych statystycznych, aby pomóc w wyborze przyszłych usprawnień Cura. Część twoich ustawień i preferencji jest wysyłana, a także wersja Cury i kod modelu który tniesz."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:50
msgctxt "@action:button"
msgid "Disable"
-msgstr ""
+msgstr "Wyłącz"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:51
msgctxt "@action:tooltip"
msgid "Don't allow Cura to send anonymized usage statistics. You can enable it again in the preferences."
-msgstr ""
+msgstr "Nie zezwalaj Cura na wysyłanie anonimowych danych statystycznych. Możesz to włączyć ponownie w preferencjach."
#: /home/ruben/Projects/Cura/plugins/LegacyProfileReader/__init__.py:14
msgctxt "@item:inlistbox"
@@ -742,7 +757,7 @@ msgstr "Profile Cura 15.04 "
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/__init__.py:15
msgctxt "@item:inlistbox"
msgid "Blender file"
-msgstr ""
+msgstr "Plik Blender"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/CadIntegrationUtils/CommonReader.py:199
msgctxt "@info:status"
@@ -750,6 +765,8 @@ msgid ""
"Could not export using \"{}\" quality!\n"
"Felt back to \"{}\"."
msgstr ""
+"Nie można wyeksportować używając \"{}\" jakości!\n"
+"Powrócono do \"{}\"."
#: /home/ruben/Projects/Cura/plugins/GCodeProfileReader/__init__.py:14
#: /home/ruben/Projects/Cura/plugins/GCodeReader/__init__.py:14
@@ -936,12 +953,12 @@ msgstr "Profile Cura"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:12
msgctxt "@item:inmenu"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Asystent Profilu"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:17
msgctxt "@item:inlistbox"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Asystent Profilu"
#: /home/ruben/Projects/Cura/plugins/3MFWriter/__init__.py:30
msgctxt "@item:inlistbox"
@@ -1139,13 +1156,13 @@ msgstr "Nie udało się zaimportować profilu z {0}: or !"
msgid "This profile {0} contains incorrect data, could not import it."
-msgstr ""
+msgstr "Ten profil {0} zawiera błędne dane, nie można go zaimportować."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:240
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "The machine defined in profile {0} doesn't match with your current machine, could not import it."
-msgstr ""
+msgstr "Maszyna zdefiniowana w profilu {0} nie zgadza się z obecnie wybraną maszyną, nie można zaimportować."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:274
#, python-brace-format
@@ -1157,7 +1174,7 @@ msgstr "Profil zaimportowany {0}"
#, python-brace-format
msgctxt "@info:status"
msgid "File {0} does not contain any valid profile."
-msgstr ""
+msgstr "Plik {0} nie zawiera żadnego poprawnego profilu."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:280
#, python-brace-format
@@ -1185,7 +1202,7 @@ msgstr "Nie można znaleźć typu jakości {0} dla bieżącej konfiguracji."
#, python-brace-format
msgctxt "@label"
msgid "Group #{group_nr}"
-msgstr ""
+msgstr "Grupa #{group_nr}"
#: /home/ruben/Projects/Cura/cura/BuildVolume.py:100
msgctxt "@info:status"
@@ -1236,7 +1253,7 @@ msgstr "Nie można Znaleźć Lokalizacji"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:81
msgctxt "@title:window"
msgid "Crash Report"
-msgstr "Raport awarii"
+msgstr "Raport Błędu"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:94
msgctxt "@label crash message"
@@ -1245,6 +1262,9 @@ msgid ""
"
Please use the \"Send report\" button to post a bug report automatically to our servers
\n"
" "
msgstr ""
+"
Wystąpił błąd krytyczny. Proszę wysłać do nas ten Raport Błędu, aby rozwiązać ten problem
\n"
+"
Proszę użyć przycisku \"Wyślij raport\" aby wysłać raport automatycznie na nasze serwery
"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:147
msgctxt "@title:groupbox"
msgid "Error traceback"
-msgstr ""
+msgstr "Śledzenie błedu"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:214
msgctxt "@title:groupbox"
@@ -1518,7 +1538,7 @@ msgstr "Rozmiar dyszy"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:393
msgctxt "@label"
msgid "Compatible material diameter"
-msgstr ""
+msgstr "Kompatybilna średnica materiału"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:395
msgctxt "@tooltip"
@@ -1663,12 +1683,12 @@ msgstr "Rodzaj"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:233
msgctxt "@label Printer name"
msgid "Ultimaker 3"
-msgstr ""
+msgstr "Ultimaker 3"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:236
msgctxt "@label Printer name"
msgid "Ultimaker 3 Extended"
-msgstr ""
+msgstr "Ultimaker 3 Extended"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:252
msgctxt "@label"
@@ -1735,7 +1755,7 @@ msgstr "%1 nie została ustawiona do hostowania grupy podłączonych drukarek Ul
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml:55
msgctxt "@label link to connect manager"
msgid "Add/Remove printers"
-msgstr ""
+msgstr "Dodaj/Usuń drukarki"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/OpenPanelButton.qml:14
msgctxt "@info:tooltip"
@@ -1775,7 +1795,7 @@ msgstr "Utracone połączenie z drukarką"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:47
msgctxt "@label Printer status"
msgid "Unknown"
-msgstr ""
+msgstr "Nieznany"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:257
msgctxt "@label:status"
@@ -1871,62 +1891,62 @@ msgstr "Uaktywnij konfigurację"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:21
msgctxt "@title:window"
msgid "SolidWorks: Export wizard"
-msgstr ""
+msgstr "SolidWorks: Kreator eksportu"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:45
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:140
msgctxt "@action:label"
msgid "Quality:"
-msgstr ""
+msgstr "Jakość:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:78
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:179
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (3D-printing)"
-msgstr ""
+msgstr "Dobra (druk 3D)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:79
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:180
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (3D-printing)"
-msgstr ""
+msgstr "Słaba (druk 3D)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:80
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:181
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (SolidWorks)"
-msgstr ""
+msgstr "Dobra (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:81
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:182
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (SolidWorks)"
-msgstr ""
+msgstr "Słaba (Solidworks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:94
msgctxt "@text:window"
msgid "Show this dialog again"
-msgstr ""
+msgstr "Pokaż to okno ponownie"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:104
msgctxt "@action:button"
msgid "Continue"
-msgstr ""
+msgstr "Kontynuuj"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:116
msgctxt "@action:button"
msgid "Abort"
-msgstr ""
+msgstr "Przerwij"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:21
msgctxt "@title:window"
msgid "How to install Cura SolidWorks macro"
-msgstr ""
+msgstr "Jak zainstalować skróty SolidWorks dla Cura"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:62
msgctxt "@description:label"
msgid "Steps:"
-msgstr ""
+msgstr "Kroki:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:140
msgctxt "@action:button"
@@ -1934,101 +1954,103 @@ msgid ""
"Open the directory\n"
"with macro and icon"
msgstr ""
+"Otwórz folder\n"
+"ze skrótem i ikoną"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:160
msgctxt "@description:label"
msgid "Instructions:"
-msgstr ""
+msgstr "Instrukcje:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:202
msgctxt "@action:playpause"
msgid "Play"
-msgstr ""
+msgstr "Odtwórz"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:206
msgctxt "@action:playpause"
msgid "Pause"
-msgstr ""
+msgstr "Zatrzymaj"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:268
msgctxt "@action:button"
msgid "Previous Step"
-msgstr ""
+msgstr "Poprzedni Krok"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:283
msgctxt "@action:button"
msgid "Done"
-msgstr ""
+msgstr "Zrobione"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:287
msgctxt "@action:button"
msgid "Next Step"
-msgstr ""
+msgstr "Następny Krok"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:21
msgctxt "@title:window"
msgid "SolidWorks plugin: Configuration"
-msgstr ""
+msgstr "Plugin SolidWorks: Konfiguracja"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:39
msgctxt "@title:tab"
msgid "Conversion settings"
-msgstr ""
+msgstr "Konwersja ustawień"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:66
msgctxt "@label"
msgid "First choice:"
-msgstr ""
+msgstr "Pierwszy wybór:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:86
msgctxt "@text:menu"
msgid "Latest installed version (Recommended)"
-msgstr ""
+msgstr "Ostatnio zainstalowana wersja (Rekomendowana)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:95
msgctxt "@text:menu"
msgid "Default version"
-msgstr ""
+msgstr "Domyślna wersja"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:193
msgctxt "@label"
msgid "Show wizard before opening SolidWorks files"
-msgstr ""
+msgstr "Pokaż konfigurator przed otworzeniem plików SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:203
msgctxt "@label"
msgid "Automatically rotate opened file into normed orientation"
-msgstr ""
+msgstr "Automatycznie obracaj otworzone pliki do unormowanej pozycji"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:210
msgctxt "@title:tab"
msgid "Installation(s)"
-msgstr ""
+msgstr "Instalacja(-e)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:284
msgctxt "@label"
msgid "COM service found"
-msgstr ""
+msgstr "Usługa COM znaleziona"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:295
msgctxt "@label"
msgid "Executable found"
-msgstr ""
+msgstr "Znaleziono plik wykonywalny"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:306
msgctxt "@label"
msgid "COM starting"
-msgstr ""
+msgstr "Uruchamianie COM"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:317
msgctxt "@label"
msgid "Revision number"
-msgstr ""
+msgstr "Numer wydania"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:328
msgctxt "@label"
msgid "Functions available"
-msgstr ""
+msgstr "Dostępne funkcje"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:341
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:264
@@ -2214,32 +2236,32 @@ msgstr "Wygładzanie"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:38
msgctxt "@label"
msgid "Mesh Type"
-msgstr ""
+msgstr "Typ siatki"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:69
msgctxt "@label"
msgid "Normal model"
-msgstr ""
+msgstr "Normalny model"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:76
msgctxt "@label"
msgid "Print as support"
-msgstr ""
+msgstr "Drukuj jako podpora"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:84
msgctxt "@label"
msgid "Don't support overlap with other models"
-msgstr ""
+msgstr "Nie wspieraj nałożeń z innymi modelami"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:92
msgctxt "@label"
msgid "Modify settings for overlap with other models"
-msgstr ""
+msgstr "Modyfikuj ustawienia nakładania z innymi modelami"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:100
msgctxt "@label"
msgid "Modify settings for infill of other models"
-msgstr ""
+msgstr "Modyfikuj ustawienia wypełnienia innych modeli"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:333
msgctxt "@action:button"
@@ -3099,27 +3121,27 @@ msgstr "Wyślij (anonimowe) informacje o drukowaniu"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:674
msgctxt "@label"
msgid "Experimental"
-msgstr ""
+msgstr "Eksperymentalne"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:680
msgctxt "@info:tooltip"
msgid "Use multi build plate functionality"
-msgstr ""
+msgstr "Użyj funkcji wielu pól roboczych"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:685
msgctxt "@option:check"
msgid "Use multi build plate functionality (restart required)"
-msgstr ""
+msgstr "Użyj funkcji wielu pól roboczych (wymagany restart)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:694
msgctxt "@info:tooltip"
msgid "Should newly loaded models be arranged on the build plate? Used in conjunction with multi build plate (EXPERIMENTAL)"
-msgstr ""
+msgstr "Czy nowo załadowane modele powinny zostać rozłożone na platformie roboczej? Używane w połączeniu z multi platformą roboczą (EKSPERYMENTALNE)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:699
msgctxt "@option:check"
msgid "Do not arrange objects on load"
-msgstr ""
+msgstr "Nie układaj obiektów podczas ładowania"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:15
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:514
@@ -3532,7 +3554,7 @@ msgstr "Pod wpływem"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:156
msgctxt "@label"
msgid "This setting is always shared between all extruders. Changing it here will change the value for all extruders."
-msgstr ""
+msgstr "To ustawienie jest dzielone pomiędzy wszystkimi ekstruderami. Zmiana tutaj spowoduje zmianę dla wszystkich ekstruderów."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:159
msgctxt "@label"
@@ -3583,7 +3605,7 @@ msgstr "00godz. 00min."
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:359
msgctxt "@tooltip"
msgid "Time specification"
-msgstr ""
+msgstr "Specyfikacja czasu"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:441
msgctxt "@label"
@@ -3640,12 +3662,12 @@ msgstr "&Widok"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:37
msgctxt "@action:inmenu menubar:view"
msgid "&Camera position"
-msgstr ""
+msgstr "&Pozycja kamery"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:52
msgctxt "@action:inmenu menubar:view"
msgid "&Build plate"
-msgstr ""
+msgstr "&Pole robocze"
#: /home/ruben/Projects/Cura/resources/qml/Menus/NozzleMenu.qml:40
msgctxt "@title:menuitem %1 is the nozzle currently loaded in the printer"
@@ -3815,27 +3837,27 @@ msgstr "&Zamknij"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:114
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
-msgstr ""
+msgstr "&Widok 3D"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:121
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
-msgstr ""
+msgstr "&Widok z przodu"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:128
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
-msgstr ""
+msgstr "&Widok z góry"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:135
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
-msgstr ""
+msgstr "&Widok z lewej strony"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:142
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
-msgstr ""
+msgstr "&Widok z prawej strony"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:149
msgctxt "@action:inmenu"
@@ -3961,7 +3983,7 @@ msgstr "Przeładuj wszystkie modele"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:edit"
msgid "Arrange All Models To All Build Plates"
-msgstr ""
+msgstr "Rozłóż Wszystkie Modele na Wszystkie Platformy Robocze."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:358
msgctxt "@action:inmenu menubar:edit"
@@ -4021,7 +4043,7 @@ msgstr "Zainstalowane wtyczki..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:438
msgctxt "@action:inmenu menubar:view"
msgid "Expand/Collapse Sidebar"
-msgstr ""
+msgstr "Rozłóż/Schowaj Pasek Boczny"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:26
msgctxt "@label:PrintjobStatus"
@@ -4056,12 +4078,12 @@ msgstr "Cięcie niedostępne"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Slice current printjob"
-msgstr ""
+msgstr "Potnij aktualny wydruk"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Cancel slicing process"
-msgstr ""
+msgstr "Przerwij proces cięcia"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:183
msgctxt "@label:Printjob"
@@ -4117,7 +4139,7 @@ msgstr "Zapisz &jako..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:139
msgctxt "@title:menu menubar:file"
msgid "Save &Project..."
-msgstr ""
+msgstr "Zapisz &Project..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:162
msgctxt "@title:menu menubar:toplevel"
@@ -4356,7 +4378,7 @@ msgstr "Materiał"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:352
msgctxt "@label"
msgid "Check compatibility"
-msgstr ""
+msgstr "Sprawdź kompatybilność"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:372
msgctxt "@tooltip"
@@ -4366,17 +4388,17 @@ msgstr "Kliknij, aby sprawdzić zgodność materiału na Ultimaker.com."
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:211
msgctxt "@option:check"
msgid "See only current build plate"
-msgstr ""
+msgstr "Pokaż tylko aktualną platformę roboczą"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:227
msgctxt "@action:button"
msgid "Arrange to all build plates"
-msgstr ""
+msgstr "Rozłóż na wszystkich platformach roboczych"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:247
msgctxt "@action:button"
msgid "Arrange current build plate"
-msgstr ""
+msgstr "Rozłóż na obecnej platformie roboczej"
#: MachineSettingsAction/plugin.json
msgctxt "description"
@@ -4471,22 +4493,22 @@ msgstr "Drukowanie USB"
#: PrepareStage/plugin.json
msgctxt "description"
msgid "Provides a prepare stage in Cura."
-msgstr ""
+msgstr "Zapewnia etap przygotowania w Cura."
#: PrepareStage/plugin.json
msgctxt "name"
msgid "Prepare Stage"
-msgstr ""
+msgstr "Etap Przygotowania"
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "description"
msgid "Provides an edit window for direct script editing."
-msgstr ""
+msgstr "Zapewnia okno edycji dla bezpośredniego edytowania skryptów."
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "name"
msgid "Live scripting tool"
-msgstr ""
+msgstr "Narzędzie pisania skryptów na żywo."
#: RemovableDriveOutputDevice/plugin.json
msgctxt "description"
@@ -4511,12 +4533,12 @@ msgstr "Połączenie Sieciowe UM3"
#: MonitorStage/plugin.json
msgctxt "description"
msgid "Provides a monitor stage in Cura."
-msgstr ""
+msgstr "Zapewnia etap monitorowania w Cura."
#: MonitorStage/plugin.json
msgctxt "name"
msgid "Monitor Stage"
-msgstr ""
+msgstr "Etap Monitorowania"
#: FirmwareUpdateChecker/plugin.json
msgctxt "description"
@@ -4531,7 +4553,7 @@ msgstr "Sprawdzacz Aktualizacji Oprogramowania"
#: CuraSolidWorksPlugin/plugin.json
msgctxt "description"
msgid "Gives you the possibility to open certain files using SolidWorks itself. Conversion is done by this plugin and additional optimizations."
-msgstr ""
+msgstr "Daje Tobie możliwość otwierania plików używają samego SolidWorks. Konwersja jest wykonywana przez ten plugin i dodatkowo optymalizowana."
#: CuraSolidWorksPlugin/plugin.json
msgctxt "name"
@@ -4601,12 +4623,12 @@ msgstr "Czytnik Profili Starszej Cura"
#: CuraBlenderPlugin/plugin.json
msgctxt "description"
msgid "Helps to open Blender files directly in Cura."
-msgstr ""
+msgstr "Pomaga w otwieraniu plików Blender bezpośrednio w Cura."
#: CuraBlenderPlugin/plugin.json
msgctxt "name"
msgid "Blender Integration (experimental)"
-msgstr ""
+msgstr "Integracja z Blenderem (eksperymentalny)"
#: GCodeProfileReader/plugin.json
msgctxt "description"
@@ -4771,12 +4793,12 @@ msgstr "Cura Profile Writer"
#: CuraPrintProfileCreator/plugin.json
msgctxt "description"
msgid "Allows material manufacturers to create new material and quality profiles using a drop-in UI."
-msgstr ""
+msgstr "Pozwala twórcą materiałów na tworzenie nowych profili materiałów i jakości używając rozwijanego menu."
#: CuraPrintProfileCreator/plugin.json
msgctxt "name"
msgid "Print Profile Assistant"
-msgstr ""
+msgstr "Asystent Profilów Druku"
#: 3MFWriter/plugin.json
msgctxt "description"
diff --git a/resources/i18n/pl_PL/fdmprinter.def.json.po b/resources/i18n/pl_PL/fdmprinter.def.json.po
index 5d886b3789..989967bd75 100644
--- a/resources/i18n/pl_PL/fdmprinter.def.json.po
+++ b/resources/i18n/pl_PL/fdmprinter.def.json.po
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: Cura 3.0\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
"POT-Creation-Date: 2017-08-02 16:53+0000\n"
-"PO-Revision-Date: 2017-11-22 19:41+0100\n"
+"PO-Revision-Date: 2018-02-10 14:03+0100\n"
"Last-Translator: 'Jaguś' Paweł Jagusiak and Andrzej 'anraf1001' Rafalski\n"
"Language-Team: reprapy.pl\n"
"Language: pl_PL\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 2.0.4\n"
+"X-Generator: Poedit 2.0.6\n"
#: fdmprinter.def.json
msgctxt "machine_settings label"
@@ -353,12 +353,12 @@ msgstr "Repetier"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract label"
msgid "Firmware Retraction"
-msgstr ""
+msgstr "Retrakcja Programowa"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract description"
msgid "Whether to use firmware retract commands (G10/G11) instead of using the E property in G1 commands to retract the material."
-msgstr ""
+msgstr "Używaj komend retrakcji (G10/G11) zamiast używać współrzędną E w komendzie G1, aby wycofać materiał."
#: fdmprinter.def.json
msgctxt "machine_disallowed_areas label"
@@ -1053,12 +1053,12 @@ msgstr "Wszędzie"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps label"
msgid "Filter Out Tiny Gaps"
-msgstr ""
+msgstr "Filtruj Małe Luki"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps description"
msgid "Filter out tiny gaps to reduce blobs on outside of model."
-msgstr ""
+msgstr "Filtruj małe luki, aby zredukować bloby na zewnątrz modelu."
#: fdmprinter.def.json
msgctxt "fill_outline_gaps label"
@@ -1443,7 +1443,7 @@ msgstr "Przesunięcie Wypełn. w Osi X"
#: fdmprinter.def.json
msgctxt "infill_offset_x description"
msgid "The infill pattern is moved this distance along the X axis."
-msgstr ""
+msgstr "Wzór wypełnienia jest przesunięty o tę odległość wzdłuż osi X."
#: fdmprinter.def.json
msgctxt "infill_offset_y label"
@@ -1453,7 +1453,7 @@ msgstr "Przesunięcie Wypełn. w Osi Y"
#: fdmprinter.def.json
msgctxt "infill_offset_y description"
msgid "The infill pattern is moved this distance along the Y axis."
-msgstr ""
+msgstr "Wzór wypełnienia jest przesunięty o tę odległość wzdłuż osi Y."
#: fdmprinter.def.json
msgctxt "sub_div_rad_add label"
@@ -1473,7 +1473,7 @@ msgstr "Procent Nałożenia Wypełn."
#: fdmprinter.def.json
msgctxt "infill_overlap description"
msgid "The amount of overlap between the infill and the walls as a percentage of the infill line width. A slight overlap allows the walls to connect firmly to the infill."
-msgstr ""
+msgstr "Ilość nałożenia pomiędzy wypełnieniem i ścianami w procentach szerokości linii wypełnienia. Delikatne nałożenie pozwala na lepsze połączenie ścian z wypełnieniem."
#: fdmprinter.def.json
msgctxt "infill_overlap_mm label"
@@ -1493,7 +1493,7 @@ msgstr "Procent Nakładania się Skóry"
#: fdmprinter.def.json
msgctxt "skin_overlap description"
msgid "The amount of overlap between the skin and the walls as a percentage of the skin line width. A slight overlap allows the walls to connect firmly to the skin. This is a percentage of the average line widths of the skin lines and the innermost wall."
-msgstr ""
+msgstr "Ilość nałożenia pomiędzy skórą a ścianami w procentach szerokości linii skóry. Delikatne nałożenie pozawala na lepsze połączenie się ścian ze skórą. Jest to procent średniej szerokości linii skóry i wewnętrznej ściany."
#: fdmprinter.def.json
msgctxt "skin_overlap_mm label"
@@ -1723,7 +1723,7 @@ msgstr "Temperatura Stołu"
#: fdmprinter.def.json
msgctxt "material_bed_temperature description"
msgid "The temperature used for the heated build plate. If this is 0, the bed temperature will not be adjusted."
-msgstr ""
+msgstr "Temperatura stosowana dla podgrzewanej platformy roboczej. Jeżeli jest ustawione 0, temperatura stołu nie będzie ustawiona."
#: fdmprinter.def.json
msgctxt "material_bed_temperature_layer_0 label"
@@ -4267,82 +4267,82 @@ msgstr "eksperymentalne!"
#: fdmprinter.def.json
msgctxt "support_tree_enable label"
msgid "Tree Support"
-msgstr ""
+msgstr "Drzewne Podpory"
#: fdmprinter.def.json
msgctxt "support_tree_enable description"
msgid "Generate a tree-like support with branches that support your print. This may reduce material usage and print time, but greatly increases slicing time."
-msgstr ""
+msgstr "Generuj drzewiaste podpory z gałęziami podpierającymi wydruk. Może to zredukować zużycie materiału i czas wydruku, ale bardzo zwiększa czas cięcia."
#: fdmprinter.def.json
msgctxt "support_tree_angle label"
msgid "Tree Support Branch Angle"
-msgstr ""
+msgstr "Kąt Gałęzi Drzewnej Podpory"
#: fdmprinter.def.json
msgctxt "support_tree_angle description"
msgid "The angle of the branches. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach."
-msgstr ""
+msgstr "Kąt gałęzi. Użyj mniejszego kąta, aby były bardziej pionowe i stabilne. Użyj większego kąta, aby mieć większy zasięg."
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance label"
msgid "Tree Support Branch Distance"
-msgstr ""
+msgstr "Odległość Gałęzi Drzewiastej Podpory"
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance description"
msgid "How far apart the branches need to be when they touch the model. Making this distance small will cause the tree support to touch the model at more points, causing better overhang but making support harder to remove."
-msgstr ""
+msgstr "W jakich odległościach powinny znajdować się gałęzie kiedy dotykają modelu. Mały dystans spowoduje więcej punktów podparcia, co da lepsze nawisy, ale utrudni usuwanie podpór."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter label"
msgid "Tree Support Branch Diameter"
-msgstr ""
+msgstr "Średnica Gałęzi Drzewiastej Podpory"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter description"
msgid "The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this."
-msgstr ""
+msgstr "Średnica najcieńszej gałęzi drzewiastej podpory. Grubsze gałęzie są bardziej sztywne. Gałęzie bliżej podłoża będą grubsze od tego."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle label"
msgid "Tree Support Branch Diameter Angle"
-msgstr ""
+msgstr "Kąt Średnicy Gałęzi Drzewiastej Podpory"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle description"
msgid "The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the tree support."
-msgstr ""
+msgstr "Kąt średnicy gałęzi, które stają się grubsze bliżej podłoża. Kąt 0 spowoduje równą grubość na całej długości gałęzi. Delikatny kąt może spowodować lepszą stabilność podpór."
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution label"
msgid "Tree Support Collision Resolution"
-msgstr ""
+msgstr "Rozdzielczość Kolizji Drzewiastej Podpory"
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution description"
msgid "Resolution to compute collisions with to avoid hitting the model. Setting this lower will produce more accurate trees that fail less often, but increases slicing time dramatically."
-msgstr ""
+msgstr "Rozdzielczość przeliczania kolizji, aby unikać zderzeń z modelem. Ustawienie niższej wartości spowoduje bardziej dokładne drzewa, które rzadziej zawodzą, ale zwiększa to drastycznie czas cięcia."
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness label"
msgid "Tree Support Wall Thickness"
-msgstr ""
+msgstr "Grubość Ściany Drzewiastej Podpory"
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness description"
msgid "The thickness of the walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "Grubość ścian gałęzi drzewiastej podpory. Grubsze ściany drukują się dłużej, ale nie odpadają tak łatwo."
#: fdmprinter.def.json
msgctxt "support_tree_wall_count label"
msgid "Tree Support Wall Line Count"
-msgstr ""
+msgstr "Liczba Linii Ściany Drzewiastej Podpory"
#: fdmprinter.def.json
msgctxt "support_tree_wall_count description"
msgid "The number of walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "Liczba ścian gałęzi drzewiastej podpory. Grubsze ściany drukują się dłużej , ale nie odpadają tak łatwo."
#: fdmprinter.def.json
msgctxt "slicing_tolerance label"
@@ -4417,12 +4417,12 @@ msgstr "Lista całkowitych kierunków linii używana kiedy skóra górnej powier
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization label"
msgid "Infill Travel Optimization"
-msgstr ""
+msgstr "Optymalizacja Ruchów Jałowych Wypełnienia"
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization description"
msgid "When enabled, the order in which the infill lines are printed is optimized to reduce the distance travelled. The reduction in travel time achieved very much depends on the model being sliced, infill pattern, density, etc. Note that, for some models that have many small areas of infill, the time to slice the model may be greatly increased."
-msgstr ""
+msgstr "Kiedy włączone, kolejność drukowania linii wypełnienia jest optymalizowana tak, aby zredukować odległości ruchów jałowych. Osiągnięta redukcja czasu ruchów jałowych zależy od ciętego modelu, wzory wypełnienia, gęstości itd. Zauważ, że dla niektórych modeli, które mają małe obszary wypełnienia, czas ciecia modelu może się bardzo wydłużyć."
#: fdmprinter.def.json
msgctxt "material_flow_dependent_temperature label"
@@ -5056,42 +5056,42 @@ msgstr "Odległość między dyszą a liniami skierowanymi w dół. Większe prz
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled label"
msgid "Use adaptive layers"
-msgstr ""
+msgstr "Użyj zmiennych warstw"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled description"
msgid "Adaptive layers computes the layer heights depending on the shape of the model."
-msgstr ""
+msgstr "Zmienne warstwy obliczają wysokości warstw w zależności od kształtu modelu."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation label"
msgid "Adaptive layers maximum variation"
-msgstr ""
+msgstr "Maks. zmiana zmiennych warstw"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation description"
msgid "The maximum allowed height different from the base layer height in mm."
-msgstr ""
+msgstr "Maksymalna dozwolona różnica wysokości od podstawowej wysokości warstwy w mm."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step label"
msgid "Adaptive layers variation step size"
-msgstr ""
+msgstr "Krok zmian zmiennych warstw"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step description"
msgid "The difference in height of the next layer height compared to the previous one."
-msgstr ""
+msgstr "Różnica w wysokości pomiędzy następną wysokością warstwy i poprzednią."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold label"
msgid "Adaptive layers threshold"
-msgstr ""
+msgstr "Opóźnienie zmiennych warstw"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold description"
msgid "Threshold whether to use a smaller layer or not. This number is compared to the tan of the steepest slope in a layer."
-msgstr ""
+msgstr "Opóźnienie w wyborze, czy użyć mniejszej warstwy, czy nie. Ta liczba jest porównywana do najbardziej stromego nachylenia na warstwie."
#: fdmprinter.def.json
msgctxt "command_line_settings label"
diff --git a/resources/i18n/pt_BR/cura.po b/resources/i18n/pt_BR/cura.po
index 947ee3416d..9000a6ff35 100644
--- a/resources/i18n/pt_BR/cura.po
+++ b/resources/i18n/pt_BR/cura.po
@@ -1,14 +1,14 @@
# Cura
-# Copyright (C) 2017 Ultimaker
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
-"PO-Revision-Date: 2017-12-04 10:20-0300\n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
+"PO-Revision-Date: 2018-02-12 10:20-0300\n"
"Last-Translator: Cláudio Sampaio \n"
"Language-Team: Cláudio Sampaio and CoderSquirrel \n"
"Language: pt_BR\n"
@@ -188,7 +188,7 @@ msgstr "Firmware da Impressora"
#: /home/ruben/Projects/Cura/plugins/PrepareStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Prepare"
-msgstr ""
+msgstr "Preparar"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:23
msgctxt "@action:button Preceded by 'Ready to'."
@@ -561,7 +561,7 @@ msgstr "Abrir a interface de trabalhos de impressão em seu navegador."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:239
msgctxt "@label Printer name"
msgid "Unknown"
-msgstr ""
+msgstr "Desconhecida"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:505
#, python-brace-format
@@ -596,7 +596,7 @@ msgstr "Conectar pela rede"
#: /home/ruben/Projects/Cura/plugins/MonitorStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Monitor"
-msgstr ""
+msgstr "Monitor"
#: /home/ruben/Projects/Cura/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py:66
#, python-brace-format
@@ -623,7 +623,7 @@ msgstr "Não foi possível acessar informação de atualização."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:579
msgctxt "@info:status"
msgid "SolidWorks reported errors, while opening your file. We recommend to solve these issues inside SolidWorks itself."
-msgstr ""
+msgstr "O SolidWorks relatou problemas ao abrir seu arquivo. Recomendamos resolver tais problemas dentro do próprio SolidWorks."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:591
msgctxt "@info:status"
@@ -632,6 +632,9 @@ msgid ""
"\n"
" Thanks!."
msgstr ""
+"Não foram encontrados modelos dentro de seu desenho. Poderia verificar seu conteúdo novamente e se assegurar que uma parte ou montagem está incluída?\n"
+"\n"
+" Obrigado!."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:595
msgctxt "@info:status"
@@ -640,6 +643,9 @@ msgid ""
"\n"
"Sorry!"
msgstr ""
+"Foi encontrado mais de uma parte ou montagem dentro de seu desenho. Atualmente só suportamos desenhos com exatamente uma parte ou montagem dentro.\n"
+"\n"
+"Desculpe!"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:25
msgctxt "@item:inlistbox"
@@ -654,7 +660,7 @@ msgstr "Arquivo de montagem de SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:33
msgctxt "@item:inlistbox"
msgid "SolidWorks drawing file"
-msgstr ""
+msgstr "Arquivo de desenho do SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:48
msgctxt "@info:status"
@@ -665,6 +671,11 @@ msgid ""
"With kind regards\n"
" - Thomas Karl Pietrowski"
msgstr ""
+"Caro cliente,\n"
+"Não foi encontrada uma intalação válida de SolidWorks no seu sistema. Isso significa que ou o SolidWorks não está instalado ou você nào tem licença válida. Por favor se assegure que rodar o Solidworks funciona sem problemas e/ou contate seu suporte.\n"
+"\n"
+"Atenciosamente\n"
+" - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:57
msgctxt "@info:status"
@@ -675,6 +686,11 @@ msgid ""
"With kind regards\n"
" - Thomas Karl Pietrowski"
msgstr ""
+"Caro cliente,\n"
+"Você está no momento rodando este complemento em um sistema operacional diferente de Windows. Este complemento só funcionará no Windows com o SolidWorks instalado e com licença válida. Por favor instale este complemento em uma máquina Windows com o SolidWorks instalado.\n"
+"\n"
+"Atenciosamente\n"
+" - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:70
msgid "Configure"
@@ -682,7 +698,7 @@ msgstr "Configure"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:71
msgid "Installation guide for SolidWorks macro"
-msgstr ""
+msgstr "Guia de Instalação para macro do SolidWorks"
#: /home/ruben/Projects/Cura/plugins/SimulationView/__init__.py:14
msgctxt "@item:inlistbox"
@@ -706,7 +722,7 @@ msgstr "Modificar G-Code"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:43
msgctxt "@info"
msgid "Cura collects anonymized usage statistics."
-msgstr ""
+msgstr "O Cura coleta estatísticas anônimas de uso."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:46
msgctxt "@info:title"
@@ -716,22 +732,22 @@ msgstr "Coletando Dados"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:48
msgctxt "@action:button"
msgid "Allow"
-msgstr ""
+msgstr "Permitir"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:49
msgctxt "@action:tooltip"
msgid "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing."
-msgstr ""
+msgstr "Permite que o Cura envie estatísticas anônimas de uso para ajudar a priorizar futuras melhorias ao software. Algumas de suas preferências e ajustes são enviados junto à versão atual do Cura e um hash dos modelos que estão sendo fatiados."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:50
msgctxt "@action:button"
msgid "Disable"
-msgstr ""
+msgstr "Desabilitar"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:51
msgctxt "@action:tooltip"
msgid "Don't allow Cura to send anonymized usage statistics. You can enable it again in the preferences."
-msgstr ""
+msgstr "Não permitir que o Cura envie estatísticas anônimas de uso. Você pode habilitar novamente nas preferências."
#: /home/ruben/Projects/Cura/plugins/LegacyProfileReader/__init__.py:14
msgctxt "@item:inlistbox"
@@ -741,7 +757,7 @@ msgstr "Perfis do Cura 15.04"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/__init__.py:15
msgctxt "@item:inlistbox"
msgid "Blender file"
-msgstr ""
+msgstr "Arquivo do Blender"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/CadIntegrationUtils/CommonReader.py:199
msgctxt "@info:status"
@@ -749,6 +765,8 @@ msgid ""
"Could not export using \"{}\" quality!\n"
"Felt back to \"{}\"."
msgstr ""
+"Não foi possível exportar usando qualidade \"{}\"!\n"
+"Foi usada a \"{}\"."
#: /home/ruben/Projects/Cura/plugins/GCodeProfileReader/__init__.py:14
#: /home/ruben/Projects/Cura/plugins/GCodeReader/__init__.py:14
@@ -935,12 +953,12 @@ msgstr "Perfil do Cura"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:12
msgctxt "@item:inmenu"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Assistente de Perfil"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:17
msgctxt "@item:inlistbox"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Assistente de Perfil"
#: /home/ruben/Projects/Cura/plugins/3MFWriter/__init__.py:30
msgctxt "@item:inlistbox"
@@ -1138,13 +1156,13 @@ msgstr "Falha ao importa perfil de {0}: {1} or !"
msgid "This profile {0} contains incorrect data, could not import it."
-msgstr ""
+msgstr "Este perfil {0} contém dados incorretos, não foi possível importá-lo."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:240
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "The machine defined in profile {0} doesn't match with your current machine, could not import it."
-msgstr ""
+msgstr "A máquina definida no perfil {0} não corresponde à sua máquina atual, não foi possível importá-lo."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:274
#, python-brace-format
@@ -1156,7 +1174,7 @@ msgstr "Perfil {0} importado com sucesso"
#, python-brace-format
msgctxt "@info:status"
msgid "File {0} does not contain any valid profile."
-msgstr ""
+msgstr "Arquivo {0} não contém nenhum perfil válido."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:280
#, python-brace-format
@@ -1184,7 +1202,7 @@ msgstr "Não foi possível encontrar tipo de qualidade {0} para a configuração
#, python-brace-format
msgctxt "@label"
msgid "Group #{group_nr}"
-msgstr ""
+msgstr "Grupo #{group_nr}"
#: /home/ruben/Projects/Cura/cura/BuildVolume.py:100
msgctxt "@info:status"
@@ -1244,6 +1262,9 @@ msgid ""
"
Please use the \"Send report\" button to post a bug report automatically to our servers
\n"
" "
msgstr ""
+"
Um erro fatal ocorreu. Por favor nos envie este Relatório de Erro para consertar o problema
\n"
+"
Por favor use o botão \"Enviar relatório\" para publicar um relatório de erro automaticamente nos nossos servidores
"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:147
msgctxt "@title:groupbox"
msgid "Error traceback"
-msgstr ""
+msgstr "Traceback do erro"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:214
msgctxt "@title:groupbox"
@@ -1517,7 +1538,7 @@ msgstr "Tamanho do bico"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:393
msgctxt "@label"
msgid "Compatible material diameter"
-msgstr ""
+msgstr "Diâmetro de material compatível"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:395
msgctxt "@tooltip"
@@ -1662,12 +1683,12 @@ msgstr "Tipo"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:233
msgctxt "@label Printer name"
msgid "Ultimaker 3"
-msgstr ""
+msgstr "Ultimaker 3"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:236
msgctxt "@label Printer name"
msgid "Ultimaker 3 Extended"
-msgstr ""
+msgstr "Ultimaker 3 Extended"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:252
msgctxt "@label"
@@ -1734,7 +1755,7 @@ msgstr "%1 não está configurada para hospedar um grupo de impressora Ultimaker
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml:55
msgctxt "@label link to connect manager"
msgid "Add/Remove printers"
-msgstr ""
+msgstr "Adicionar/Remover impressoras"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/OpenPanelButton.qml:14
msgctxt "@info:tooltip"
@@ -1774,7 +1795,7 @@ msgstr "A conexão à impressora foi perdida"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:47
msgctxt "@label Printer status"
msgid "Unknown"
-msgstr ""
+msgstr "Desconhecido"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:257
msgctxt "@label:status"
@@ -1870,62 +1891,62 @@ msgstr "Ativar Configuração"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:21
msgctxt "@title:window"
msgid "SolidWorks: Export wizard"
-msgstr ""
+msgstr "SolidWorks: Assistente de Exportação"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:45
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:140
msgctxt "@action:label"
msgid "Quality:"
-msgstr ""
+msgstr "Qualidade"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:78
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:179
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (3D-printing)"
-msgstr ""
+msgstr "Fina (impressão-3D)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:79
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:180
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (3D-printing)"
-msgstr ""
+msgstr "Baixa"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:80
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:181
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (SolidWorks)"
-msgstr ""
+msgstr "Fina (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:81
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:182
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (SolidWorks)"
-msgstr ""
+msgstr "Baixa (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:94
msgctxt "@text:window"
msgid "Show this dialog again"
-msgstr ""
+msgstr "Mostrar este diálogo novamente"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:104
msgctxt "@action:button"
msgid "Continue"
-msgstr ""
+msgstr "Continuar"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:116
msgctxt "@action:button"
msgid "Abort"
-msgstr ""
+msgstr "Abortar"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:21
msgctxt "@title:window"
msgid "How to install Cura SolidWorks macro"
-msgstr ""
+msgstr "Como instalar a macro de SolidWorks do Cura"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:62
msgctxt "@description:label"
msgid "Steps:"
-msgstr ""
+msgstr "Passos:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:140
msgctxt "@action:button"
@@ -1933,101 +1954,103 @@ msgid ""
"Open the directory\n"
"with macro and icon"
msgstr ""
+"Abrir o diretório\n"
+"com a macro e o ícone"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:160
msgctxt "@description:label"
msgid "Instructions:"
-msgstr ""
+msgstr "Instruções:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:202
msgctxt "@action:playpause"
msgid "Play"
-msgstr ""
+msgstr "Tocar"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:206
msgctxt "@action:playpause"
msgid "Pause"
-msgstr ""
+msgstr "Pausar"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:268
msgctxt "@action:button"
msgid "Previous Step"
-msgstr ""
+msgstr "Passo Anterior"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:283
msgctxt "@action:button"
msgid "Done"
-msgstr ""
+msgstr "Finalizado"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:287
msgctxt "@action:button"
msgid "Next Step"
-msgstr ""
+msgstr "Passo Seguinte"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:21
msgctxt "@title:window"
msgid "SolidWorks plugin: Configuration"
-msgstr ""
+msgstr "Complemento do SolidWorks: Configuração"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:39
msgctxt "@title:tab"
msgid "Conversion settings"
-msgstr ""
+msgstr "Ajustes de conversão"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:66
msgctxt "@label"
msgid "First choice:"
-msgstr ""
+msgstr "Primeira escolha:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:86
msgctxt "@text:menu"
msgid "Latest installed version (Recommended)"
-msgstr ""
+msgstr "Última versão instalada (Recomendado)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:95
msgctxt "@text:menu"
msgid "Default version"
-msgstr ""
+msgstr "Versão default"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:193
msgctxt "@label"
msgid "Show wizard before opening SolidWorks files"
-msgstr ""
+msgstr "Mostrar o assistente antes de abrir arquivos do SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:203
msgctxt "@label"
msgid "Automatically rotate opened file into normed orientation"
-msgstr ""
+msgstr "Rotacionar automaticamente o arquivo aberto em orientação normalizada"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:210
msgctxt "@title:tab"
msgid "Installation(s)"
-msgstr ""
+msgstr "Instalações"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:284
msgctxt "@label"
msgid "COM service found"
-msgstr ""
+msgstr "Serviço COM encontrado"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:295
msgctxt "@label"
msgid "Executable found"
-msgstr ""
+msgstr "Executável encontrado"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:306
msgctxt "@label"
msgid "COM starting"
-msgstr ""
+msgstr "COM iniciando"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:317
msgctxt "@label"
msgid "Revision number"
-msgstr ""
+msgstr "Número de revisão"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:328
msgctxt "@label"
msgid "Functions available"
-msgstr ""
+msgstr "Funções disponíveis"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:341
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:264
@@ -2213,32 +2236,32 @@ msgstr "Suavização"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:38
msgctxt "@label"
msgid "Mesh Type"
-msgstr ""
+msgstr "Tipo de Malha"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:69
msgctxt "@label"
msgid "Normal model"
-msgstr ""
+msgstr "Modelo normal"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:76
msgctxt "@label"
msgid "Print as support"
-msgstr ""
+msgstr "Imprimir como suporte"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:84
msgctxt "@label"
msgid "Don't support overlap with other models"
-msgstr ""
+msgstr "Não suportar sobreposição com outros modelos"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:92
msgctxt "@label"
msgid "Modify settings for overlap with other models"
-msgstr ""
+msgstr "Modificar ajustes para sobrepor com outros modelos"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:100
msgctxt "@label"
msgid "Modify settings for infill of other models"
-msgstr ""
+msgstr "Modificar ajustes para preenchimento de outros modelos"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:333
msgctxt "@action:button"
@@ -3098,27 +3121,27 @@ msgstr "Enviar informação (anônima) de impressão."
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:674
msgctxt "@label"
msgid "Experimental"
-msgstr ""
+msgstr "Experimental"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:680
msgctxt "@info:tooltip"
msgid "Use multi build plate functionality"
-msgstr ""
+msgstr "Usar funcionalidade de plataforma múltipla de impressão"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:685
msgctxt "@option:check"
msgid "Use multi build plate functionality (restart required)"
-msgstr ""
+msgstr "Usar funcionalidade de plataforma múltipla de impressão (reinício requerido)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:694
msgctxt "@info:tooltip"
msgid "Should newly loaded models be arranged on the build plate? Used in conjunction with multi build plate (EXPERIMENTAL)"
-msgstr ""
+msgstr "Novos modelos carregados devem ser posicionados na plataforma de impressão? Usado em conjunção com plataforma múltipla de impressão (EXPERIMENTAL)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:699
msgctxt "@option:check"
msgid "Do not arrange objects on load"
-msgstr ""
+msgstr "Não posicionar objetos ao carregar."
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:15
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:514
@@ -3531,7 +3554,7 @@ msgstr "Afetado Por"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:156
msgctxt "@label"
msgid "This setting is always shared between all extruders. Changing it here will change the value for all extruders."
-msgstr ""
+msgstr "Este ajuste é sempre compartilhado entre todos os extrusores. Modificá-lo aqui mudará o valor para todos."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:159
msgctxt "@label"
@@ -3580,7 +3603,7 @@ msgstr "00h 00min"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:359
msgctxt "@tooltip"
msgid "Time specification"
-msgstr ""
+msgstr "Especificação de tempo"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:441
msgctxt "@label"
@@ -3637,12 +3660,12 @@ msgstr "&Ver"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:37
msgctxt "@action:inmenu menubar:view"
msgid "&Camera position"
-msgstr ""
+msgstr "Posição da &câmera"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:52
msgctxt "@action:inmenu menubar:view"
msgid "&Build plate"
-msgstr ""
+msgstr "Plataforma de Impressão (&B)"
#: /home/ruben/Projects/Cura/resources/qml/Menus/NozzleMenu.qml:40
msgctxt "@title:menuitem %1 is the nozzle currently loaded in the printer"
@@ -3797,7 +3820,7 @@ msgstr "A<ernar Tela Cheia"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:86
msgctxt "@action:inmenu menubar:edit"
msgid "&Undo"
-msgstr "Des&fazer"
+msgstr "Desfazer (&U)"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:96
msgctxt "@action:inmenu menubar:edit"
@@ -3807,32 +3830,32 @@ msgstr "&Refazer"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:106
msgctxt "@action:inmenu menubar:file"
msgid "&Quit"
-msgstr "&Sair"
+msgstr "Sair (&Q)"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:114
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
-msgstr ""
+msgstr "Visão &3D"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:121
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
-msgstr ""
+msgstr "Visão &Frontal"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:128
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
-msgstr ""
+msgstr "Visão Superior (&T)"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:135
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
-msgstr ""
+msgstr "Visão do Lado Esquerdo (&L)"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:142
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
-msgstr ""
+msgstr "Visão do Lado Direito (&R)"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:149
msgctxt "@action:inmenu"
@@ -3857,7 +3880,7 @@ msgstr "Administrar Materiais..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:177
msgctxt "@action:inmenu menubar:profile"
msgid "&Update profile with current settings/overrides"
-msgstr "&Atualizar perfil com valores e sobrepujanças atuais"
+msgstr "At&ualizar perfil com valores e sobrepujanças atuais"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:185
msgctxt "@action:inmenu menubar:profile"
@@ -3887,7 +3910,7 @@ msgstr "Relatar um &Bug"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:226
msgctxt "@action:inmenu menubar:help"
msgid "&About..."
-msgstr "S&obre..."
+msgstr "Sobre (&A)..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:233
msgctxt "@action:inmenu menubar:edit"
@@ -3948,17 +3971,17 @@ msgstr "&Selecionar Todos Os Modelos"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:332
msgctxt "@action:inmenu menubar:edit"
msgid "&Clear Build Plate"
-msgstr "&Esvaziar a mesa de impressão"
+msgstr "Esvaziar a Mesa de Impressão (&C)"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:342
msgctxt "@action:inmenu menubar:file"
msgid "Re&load All Models"
-msgstr "&Recarregar Todos Os Modelos"
+msgstr "Recarregar Todos Os Mode&los"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:edit"
msgid "Arrange All Models To All Build Plates"
-msgstr ""
+msgstr "Posicionar Todos os Modelos em Todas as Plataformas de Impressão"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:358
msgctxt "@action:inmenu menubar:edit"
@@ -3983,7 +4006,7 @@ msgstr "Remover as &Transformações de Todos Os Modelos"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:387
msgctxt "@action:inmenu menubar:file"
msgid "&Open File(s)..."
-msgstr "Abrir Arquiv&os(s)..."
+msgstr "Abrir Arquiv&o(s)..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:395
msgctxt "@action:inmenu menubar:file"
@@ -3993,7 +4016,7 @@ msgstr "&Novo Projeto..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:402
msgctxt "@action:inmenu menubar:help"
msgid "Show Engine &Log..."
-msgstr "&Exibir o Registro do Motor de Fatiamento..."
+msgstr "Exibir o Registro do Motor de Fatiamento (&L)..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:410
msgctxt "@action:inmenu menubar:help"
@@ -4018,7 +4041,7 @@ msgstr "Complementos instalados..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:438
msgctxt "@action:inmenu menubar:view"
msgid "Expand/Collapse Sidebar"
-msgstr ""
+msgstr "Expandir/Encolher Barra Lateral"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:26
msgctxt "@label:PrintjobStatus"
@@ -4053,12 +4076,12 @@ msgstr "Fatiamento indisponível"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Slice current printjob"
-msgstr ""
+msgstr "Fatiar trabalho de impressão atual"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Cancel slicing process"
-msgstr ""
+msgstr "Cancelar processo de fatiamento"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:183
msgctxt "@label:Printjob"
@@ -4099,7 +4122,7 @@ msgstr "Ultimaker Cura"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:102
msgctxt "@title:menu menubar:toplevel"
msgid "&File"
-msgstr "&Arquivo"
+msgstr "Arquivo (&F)"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:119
msgctxt "@action:inmenu menubar:file"
@@ -4114,7 +4137,7 @@ msgstr "S&alvar Como..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:139
msgctxt "@title:menu menubar:file"
msgid "Save &Project..."
-msgstr ""
+msgstr "Salvar &Projeto..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:162
msgctxt "@title:menu menubar:toplevel"
@@ -4353,7 +4376,7 @@ msgstr "Material"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:352
msgctxt "@label"
msgid "Check compatibility"
-msgstr ""
+msgstr "Verificar compatibilidade"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:372
msgctxt "@tooltip"
@@ -4363,17 +4386,17 @@ msgstr "Clique para verificar a compatibilidade do material em Ultimaker.com."
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:211
msgctxt "@option:check"
msgid "See only current build plate"
-msgstr ""
+msgstr "Ver somente a plataforma de impressão atual"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:227
msgctxt "@action:button"
msgid "Arrange to all build plates"
-msgstr ""
+msgstr "Posicionar em todas as plataformas de impressão"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:247
msgctxt "@action:button"
msgid "Arrange current build plate"
-msgstr ""
+msgstr "Reposicionar a plataforma de impressão atual"
#: MachineSettingsAction/plugin.json
msgctxt "description"
@@ -4468,22 +4491,22 @@ msgstr "Impressão USB"
#: PrepareStage/plugin.json
msgctxt "description"
msgid "Provides a prepare stage in Cura."
-msgstr ""
+msgstr "Provê um estágio de preparação no Cura."
#: PrepareStage/plugin.json
msgctxt "name"
msgid "Prepare Stage"
-msgstr ""
+msgstr "Estágio de Preparação"
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "description"
msgid "Provides an edit window for direct script editing."
-msgstr ""
+msgstr "Provê uma janela de edição para edição direta de script."
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "name"
msgid "Live scripting tool"
-msgstr ""
+msgstr "Ferramenta de scripting integrada"
#: RemovableDriveOutputDevice/plugin.json
msgctxt "description"
@@ -4508,12 +4531,12 @@ msgstr "Conexão de Rede UM3"
#: MonitorStage/plugin.json
msgctxt "description"
msgid "Provides a monitor stage in Cura."
-msgstr ""
+msgstr "Provê um estágio de monitor no Cura."
#: MonitorStage/plugin.json
msgctxt "name"
msgid "Monitor Stage"
-msgstr ""
+msgstr "Estágio de Monitor"
#: FirmwareUpdateChecker/plugin.json
msgctxt "description"
@@ -4528,7 +4551,7 @@ msgstr "Verificador de Atualizações de Firmware"
#: CuraSolidWorksPlugin/plugin.json
msgctxt "description"
msgid "Gives you the possibility to open certain files using SolidWorks itself. Conversion is done by this plugin and additional optimizations."
-msgstr ""
+msgstr "Te dá a possibilidade de abrir certos arquivos usando o SolidWorks. A conversão é feita por este plugin junto com personalizações adicionais."
#: CuraSolidWorksPlugin/plugin.json
msgctxt "name"
@@ -4598,12 +4621,12 @@ msgstr "Leitor de Perfis de Cura Legado"
#: CuraBlenderPlugin/plugin.json
msgctxt "description"
msgid "Helps to open Blender files directly in Cura."
-msgstr ""
+msgstr "Ajuda a abrir arquivos do Blender diretamente no Cura."
#: CuraBlenderPlugin/plugin.json
msgctxt "name"
msgid "Blender Integration (experimental)"
-msgstr ""
+msgstr "Integração ao Blender (experimental)"
#: GCodeProfileReader/plugin.json
msgctxt "description"
@@ -4768,12 +4791,12 @@ msgstr "Gravador de Perfis do Cura"
#: CuraPrintProfileCreator/plugin.json
msgctxt "description"
msgid "Allows material manufacturers to create new material and quality profiles using a drop-in UI."
-msgstr ""
+msgstr "Permite que fabricantes de material criem novos perfis de material e qualidade usando uma interface drop-in."
#: CuraPrintProfileCreator/plugin.json
msgctxt "name"
msgid "Print Profile Assistant"
-msgstr ""
+msgstr "Assistente de Perfil de Impressão"
#: 3MFWriter/plugin.json
msgctxt "description"
diff --git a/resources/i18n/pt_BR/fdmextruder.def.json.po b/resources/i18n/pt_BR/fdmextruder.def.json.po
index a7609a1ceb..ad86d46edc 100644
--- a/resources/i18n/pt_BR/fdmextruder.def.json.po
+++ b/resources/i18n/pt_BR/fdmextruder.def.json.po
@@ -1,11 +1,11 @@
-# Cura JSON setting files
-# Copyright (C) 2017 Ultimaker
+# Cura
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
"POT-Creation-Date: 2017-08-02 16:53+0000\n"
"PO-Revision-Date: 2017-12-04 09:00-0300\n"
diff --git a/resources/i18n/pt_BR/fdmprinter.def.json.po b/resources/i18n/pt_BR/fdmprinter.def.json.po
index 5e2889e0e6..d42015f67c 100644
--- a/resources/i18n/pt_BR/fdmprinter.def.json.po
+++ b/resources/i18n/pt_BR/fdmprinter.def.json.po
@@ -1,14 +1,14 @@
-# Cura JSON setting files
-# Copyright (C) 2017 Ultimaker
+# Cura
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
-"PO-Revision-Date: 2017-12-04 10:20-0300\n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
+"PO-Revision-Date: 2018-02-13 02:20-0300\n"
"Last-Translator: Cláudio Sampaio \n"
"Language-Team: Cláudio Sampaio and CoderSquirrel \n"
"Language: pt_BR\n"
@@ -353,12 +353,12 @@ msgstr "Repetier"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract label"
msgid "Firmware Retraction"
-msgstr ""
+msgstr "Retração de Firmware"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract description"
msgid "Whether to use firmware retract commands (G10/G11) instead of using the E property in G1 commands to retract the material."
-msgstr ""
+msgstr "Usar ou não comandos de retração de firmware (G10/G11) ao invés de usar a propriedade E dos comandos G1 para retrair o material."
#: fdmprinter.def.json
msgctxt "machine_disallowed_areas label"
@@ -1053,12 +1053,12 @@ msgstr "Em todos os lugares"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps label"
msgid "Filter Out Tiny Gaps"
-msgstr ""
+msgstr "Filtrar Pequenas Lacunas"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps description"
msgid "Filter out tiny gaps to reduce blobs on outside of model."
-msgstr ""
+msgstr "Filtrar (rempver) pequenas lacunas para reduzir bolhas no exterior do modelo."
#: fdmprinter.def.json
msgctxt "fill_outline_gaps label"
@@ -1443,7 +1443,7 @@ msgstr "Deslocamento X do Preenchimento"
#: fdmprinter.def.json
msgctxt "infill_offset_x description"
msgid "The infill pattern is moved this distance along the X axis."
-msgstr ""
+msgstr "O padrão de preenchimento é movido por esta distância no eixo X."
#: fdmprinter.def.json
msgctxt "infill_offset_y label"
@@ -1453,7 +1453,7 @@ msgstr "Deslocamento do Preenchimento Y"
#: fdmprinter.def.json
msgctxt "infill_offset_y description"
msgid "The infill pattern is moved this distance along the Y axis."
-msgstr ""
+msgstr "O padrão de preenchimento é movido por esta distância no eixo Y."
#: fdmprinter.def.json
msgctxt "sub_div_rad_add label"
@@ -1473,7 +1473,7 @@ msgstr "Porcentagem de Sobreposição do Preenchimento"
#: fdmprinter.def.json
msgctxt "infill_overlap description"
msgid "The amount of overlap between the infill and the walls as a percentage of the infill line width. A slight overlap allows the walls to connect firmly to the infill."
-msgstr ""
+msgstr "A quantidade de sobreposição entre o preenchimento e as paredes como uma porcentagem da largura de extrusão de preenchimento. Uma leve sobreposição permite que as paredes se conectem firmemente ao preenchimento."
#: fdmprinter.def.json
msgctxt "infill_overlap_mm label"
@@ -1483,7 +1483,7 @@ msgstr "Sobreposição de Preenchimento"
#: fdmprinter.def.json
msgctxt "infill_overlap_mm description"
msgid "The amount of overlap between the infill and the walls. A slight overlap allows the walls to connect firmly to the infill."
-msgstr "Medida de sobreposição entre o preenchimento e as paredes. Uma leve sobreposição permite que as paredes fiquem firememente aderidas ao preenchimento."
+msgstr "A quantidade de sobreposição entre o preenchimento e as paredes. Uma leve sobreposição permite que as paredes se conectem firmemente ao preenchimento."
#: fdmprinter.def.json
msgctxt "skin_overlap label"
@@ -1493,7 +1493,7 @@ msgstr "Porcentagem de Sobreposição do Contorno"
#: fdmprinter.def.json
msgctxt "skin_overlap description"
msgid "The amount of overlap between the skin and the walls as a percentage of the skin line width. A slight overlap allows the walls to connect firmly to the skin. This is a percentage of the average line widths of the skin lines and the innermost wall."
-msgstr ""
+msgstr "A quantidade de sobreposição entre o contorno e as paredes como uma porcentagem da largura de extrusão do contorno. Uma leve sobreposição permite que as paredes se conectem firmemente ao contorno. É uma porcentagem das larguras de extrusão médias das linhas de contorno e a parede mais interna."
#: fdmprinter.def.json
msgctxt "skin_overlap_mm label"
@@ -1503,7 +1503,7 @@ msgstr "Sobreposição do Contorno"
#: fdmprinter.def.json
msgctxt "skin_overlap_mm description"
msgid "The amount of overlap between the skin and the walls. A slight overlap allows the walls to connect firmly to the skin."
-msgstr "Medida de sobreposição entre o contorno e as paredes. Uma ligeira sobreposição permite às paredes ficarem firmemente aderidas ao contorno."
+msgstr "A quantidade de sobreposição entre o contorno e as paredes. Uma leve sobreposição permite às paredes ficarem firmemente aderidas ao contorno."
#: fdmprinter.def.json
msgctxt "infill_wipe_dist label"
@@ -1723,7 +1723,7 @@ msgstr "Temperatura da Mesa de Impressão"
#: fdmprinter.def.json
msgctxt "material_bed_temperature description"
msgid "The temperature used for the heated build plate. If this is 0, the bed temperature will not be adjusted."
-msgstr ""
+msgstr "A temperatura usada para a plataforma de impressão aquecida. Se for 0, a temperatura da mesa não será ajustada."
#: fdmprinter.def.json
msgctxt "material_bed_temperature_layer_0 label"
@@ -4267,82 +4267,82 @@ msgstr "experimental!"
#: fdmprinter.def.json
msgctxt "support_tree_enable label"
msgid "Tree Support"
-msgstr ""
+msgstr "Suporte de Árvore"
#: fdmprinter.def.json
msgctxt "support_tree_enable description"
msgid "Generate a tree-like support with branches that support your print. This may reduce material usage and print time, but greatly increases slicing time."
-msgstr ""
+msgstr "Gera um suporte em árvore com galhos que apóiam sua impressão. Isto pode reduzir uso de material e tempo de impressão, mas aumenta bastante o tempo de fatiamento."
#: fdmprinter.def.json
msgctxt "support_tree_angle label"
msgid "Tree Support Branch Angle"
-msgstr ""
+msgstr "Ângulo do Galho do Suporte em Árvore"
#: fdmprinter.def.json
msgctxt "support_tree_angle description"
msgid "The angle of the branches. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach."
-msgstr ""
+msgstr "Ô angulo dos galhos. Use um ângulo menor para torná-los mais verticais e mais estáveis. Use um ângulo maior para aumentar o alcance."
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance label"
msgid "Tree Support Branch Distance"
-msgstr ""
+msgstr "Distância dos Galhos do Suporte em Árvore"
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance description"
msgid "How far apart the branches need to be when they touch the model. Making this distance small will cause the tree support to touch the model at more points, causing better overhang but making support harder to remove."
-msgstr ""
+msgstr "Quão distantes os galhos precisam estar quando tocam o modelo. Tornar esta distância pequena fará com que o suporte em árvore toque o modelo em mais pontos, permitindo maior sustentação mas tornando o suporte mais difícil de remover."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter label"
msgid "Tree Support Branch Diameter"
-msgstr ""
+msgstr "Diâmetro de Galho do Suporte em Árvore"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter description"
msgid "The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this."
-msgstr ""
+msgstr "O diâmetro dos galhos mais finos do suporte em árvore. Galhos mais grossos são mais resistentes. Galhos na direção da base serão mais grossos que essa medida."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle label"
msgid "Tree Support Branch Diameter Angle"
-msgstr ""
+msgstr "Ângulo do Diâmetro do Galho do Suporte em Árvore"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle description"
msgid "The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the tree support."
-msgstr ""
+msgstr "O ângulo do diâmetro dos galhos enquanto se tornam gradualmente mais grossos na direção da base. Um ângulo de 0 fará com que os galhos tenham grossura uniforme no seu comrpimento. Um ângulo levemente maior que zero pode aumentar a estabilidade do suporte em árvore."
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution label"
msgid "Tree Support Collision Resolution"
-msgstr ""
+msgstr "Resolução de Colisão do Suporte em Árvore"
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution description"
msgid "Resolution to compute collisions with to avoid hitting the model. Setting this lower will produce more accurate trees that fail less often, but increases slicing time dramatically."
-msgstr ""
+msgstr "Resolução para computar colisões com a qual evitar tocar o modelo. Ajustar valor mais baixos produzirá árvore mais precisas que falharão menos, mas aumentará o tempo de fatiamento dramaticamente."
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness label"
msgid "Tree Support Wall Thickness"
-msgstr ""
+msgstr "Espessura de Parede do Suporte em Árvore"
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness description"
msgid "The thickness of the walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "A espessura das paredes dos galhos do suporte em árvore. Paredes mais espessas tomarão mais tempo pra imprimir mas não tombarão facilmente."
#: fdmprinter.def.json
msgctxt "support_tree_wall_count label"
msgid "Tree Support Wall Line Count"
-msgstr ""
+msgstr "Número de Filetes da Parede do Suporte em Árvore"
#: fdmprinter.def.json
msgctxt "support_tree_wall_count description"
msgid "The number of walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "O número de filetes da parede dos galhos do suporte em árvore. Paredes mais espessas tomarão mais tempo pra imprimir mas não tombarão facilmente."
#: fdmprinter.def.json
msgctxt "slicing_tolerance label"
@@ -4417,12 +4417,12 @@ msgstr "Uma lista de direções inteiras de filete a usar quando as camadas supe
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization label"
msgid "Infill Travel Optimization"
-msgstr ""
+msgstr "Otimização de Percurso de Preenchimento"
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization description"
msgid "When enabled, the order in which the infill lines are printed is optimized to reduce the distance travelled. The reduction in travel time achieved very much depends on the model being sliced, infill pattern, density, etc. Note that, for some models that have many small areas of infill, the time to slice the model may be greatly increased."
-msgstr ""
+msgstr "Quando habilitado, a ordem em que os filetes de preenchimento são impressos é otimizada para reduzir a distância percorrida. A redução em tempo de percurso conseguida depende bastante do modelo sendo fatiado, do padrão de preenchimento, da densidade, etc. Note que, para alguns modelos que têm áreas bem pequenas de preenchimento, o tempo de fatiamento pode ser aumentado bastante."
#: fdmprinter.def.json
msgctxt "material_flow_dependent_temperature label"
@@ -5056,42 +5056,42 @@ msgstr "Distância entre o bico e os filetes descendentes horizontais. Espaços
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled label"
msgid "Use adaptive layers"
-msgstr ""
+msgstr "Usar camadas adaptativas"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled description"
msgid "Adaptive layers computes the layer heights depending on the shape of the model."
-msgstr ""
+msgstr "Camadas adaptativas fazem a computação das alturas de camada depender da forma do modelo."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation label"
msgid "Adaptive layers maximum variation"
-msgstr ""
+msgstr "Variação máxima das camadas adaptativas"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation description"
msgid "The maximum allowed height different from the base layer height in mm."
-msgstr ""
+msgstr "A dferença de altura máxima permitida da altura de camada base permitida, em mm."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step label"
msgid "Adaptive layers variation step size"
-msgstr ""
+msgstr "Tamanho de passo da variaçã das camadas adaptativas"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step description"
msgid "The difference in height of the next layer height compared to the previous one."
-msgstr ""
+msgstr "A diferença em tamanho da próxima camada comparada à anterior."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold label"
msgid "Adaptive layers threshold"
-msgstr ""
+msgstr "Limite das camadas adaptativas"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold description"
msgid "Threshold whether to use a smaller layer or not. This number is compared to the tan of the steepest slope in a layer."
-msgstr ""
+msgstr "Limite até onde se usa uma camada menor ou não. Este número é comparado à tangente da ladeira mais vertical da camada."
#: fdmprinter.def.json
msgctxt "command_line_settings label"
diff --git a/resources/i18n/pt_PT/cura.po b/resources/i18n/pt_PT/cura.po
index 1fae4d7adb..b8a6bc87e7 100644
--- a/resources/i18n/pt_PT/cura.po
+++ b/resources/i18n/pt_PT/cura.po
@@ -1,16 +1,16 @@
# Cura
-# Copyright (C) 2017 Ultimaker
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.1\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
-"PO-Revision-Date: 2018-01-23 19:41+0000\n"
-"Last-Translator: Paulo Miranda \n"
-"Language-Team: Bothof\n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
+"PO-Revision-Date: 2018-02-05 13:25+0100\n"
+"Last-Translator: Bothof \n"
+"Language-Team: Paulo Miranda , Portuguese \n"
"Language: pt_PT\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -199,7 +199,7 @@ msgstr "Firmware da Impressora"
#: /home/ruben/Projects/Cura/plugins/PrepareStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Prepare"
-msgstr ""
+msgstr "Preparar"
# rever!
# unidade amovível
@@ -601,7 +601,7 @@ msgstr "Abre a interface dos trabalhos de impressão no seu web browser."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:239
msgctxt "@label Printer name"
msgid "Unknown"
-msgstr ""
+msgstr "Desconhecido"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:505
#, python-brace-format
@@ -640,7 +640,7 @@ msgstr "Ligar Através da Rede"
#: /home/ruben/Projects/Cura/plugins/MonitorStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Monitor"
-msgstr ""
+msgstr "Monitorizar"
#: /home/ruben/Projects/Cura/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py:66
#, python-brace-format
@@ -667,7 +667,7 @@ msgstr "Não foi possível aceder às informações de atualização."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:579
msgctxt "@info:status"
msgid "SolidWorks reported errors, while opening your file. We recommend to solve these issues inside SolidWorks itself."
-msgstr ""
+msgstr "O SolidWorks comunicou erros ao abrir o ficheiro. Recomendamos a resolução destes problemas no SolidWorks."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:591
msgctxt "@info:status"
@@ -675,7 +675,7 @@ msgid ""
"Found no models inside your drawing. Could you please check it's content again and make sure one part or assembly is inside?\n"
"\n"
" Thanks!."
-msgstr ""
+msgstr "Não foram encontrados modelos no interior do seu desenho. Pode verificar novamente o seu conteúdo e confirmar se a peça ou conjunto está no seu interior?\n\n Obrigado!"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:595
msgctxt "@info:status"
@@ -683,7 +683,7 @@ msgid ""
"Found more then one part or assembly inside your drawing. We currently only support drawings with exactly one part or assembly inside.\n"
"\n"
"Sorry!"
-msgstr ""
+msgstr "Detetou-se mais do que uma peça ou um conjunto no interior do seu desenho. Atualmente, apenas suportamos desenhos com exatamente uma peça ou um conjunto no seu interior.\n\nLamentamos!"
# rever!
# versão PT do solidworks?
@@ -702,7 +702,7 @@ msgstr "Ficheiro SolidWorks de montagem"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:33
msgctxt "@item:inlistbox"
msgid "SolidWorks drawing file"
-msgstr ""
+msgstr "Ficheiro de desenho SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:48
msgctxt "@info:status"
@@ -712,7 +712,7 @@ msgid ""
"\n"
"With kind regards\n"
" - Thomas Karl Pietrowski"
-msgstr ""
+msgstr "Caro cliente,\nNão conseguimos encontrar uma instalação válida do SolidWorks no seu sistema. Isto significa que o SolidWorks não está instalado ou que não possui uma licença válida. Certifique-se de que o SolidWorks é executado sem problemas e/ou entre em contacto com o seu serviço de TI.\n\nAtenciosamente\n – Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:57
msgctxt "@info:status"
@@ -722,7 +722,7 @@ msgid ""
"\n"
"With kind regards\n"
" - Thomas Karl Pietrowski"
-msgstr ""
+msgstr "Caro cliente,\nEstá atualmente a executar este plug-in num sistema operativo que não o Windows. Este plug-in apenas funciona no Windows com o SolidWorks instalado e com uma licença válida. Instale este plug-in num dispositivo Windows com o SolidWorks instalado.\n\nAtenciosamente\n – Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:70
msgid "Configure"
@@ -730,12 +730,12 @@ msgstr "Configurar"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:71
msgid "Installation guide for SolidWorks macro"
-msgstr ""
+msgstr "Guia de instalação para uma macro SolidWorks"
#: /home/ruben/Projects/Cura/plugins/SimulationView/__init__.py:14
msgctxt "@item:inlistbox"
msgid "Layer view"
-msgstr ""
+msgstr "Visualização de camadas"
#: /home/ruben/Projects/Cura/plugins/SimulationView/SimulationView.py:103
msgctxt "@info:status"
@@ -756,7 +756,7 @@ msgstr "Modificar G-Code"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:43
msgctxt "@info"
msgid "Cura collects anonymized usage statistics."
-msgstr ""
+msgstr "O Cura recolhe estatísticas de utilização anónimas."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:46
msgctxt "@info:title"
@@ -766,22 +766,22 @@ msgstr "A Recolher Dados"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:48
msgctxt "@action:button"
msgid "Allow"
-msgstr ""
+msgstr "Permitir"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:49
msgctxt "@action:tooltip"
msgid "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing."
-msgstr ""
+msgstr "Permitir que o Cura envie estatísticas de utilização anónimas para ajudar a priorizar futuras melhorias do Cura. São enviadas algumas das suas preferências e definições, a versão do Cura e um hash dos modelos que está a seccionar."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:50
msgctxt "@action:button"
msgid "Disable"
-msgstr ""
+msgstr "Desativar"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:51
msgctxt "@action:tooltip"
msgid "Don't allow Cura to send anonymized usage statistics. You can enable it again in the preferences."
-msgstr ""
+msgstr "Não permitir que o Cura envie estatísticas de utilização anónimas. É possível ativá-las novamente nas suas preferências."
#: /home/ruben/Projects/Cura/plugins/LegacyProfileReader/__init__.py:14
msgctxt "@item:inlistbox"
@@ -791,14 +791,14 @@ msgstr "Perfis Cura 15.04"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/__init__.py:15
msgctxt "@item:inlistbox"
msgid "Blender file"
-msgstr ""
+msgstr "Ficheiro Blender"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/CadIntegrationUtils/CommonReader.py:199
msgctxt "@info:status"
msgid ""
"Could not export using \"{}\" quality!\n"
"Felt back to \"{}\"."
-msgstr ""
+msgstr "Não foi possível exportar utilizando a qualidade \"{}\"!\nFoi revertido para \"{}\"."
#: /home/ruben/Projects/Cura/plugins/GCodeProfileReader/__init__.py:14
#: /home/ruben/Projects/Cura/plugins/GCodeReader/__init__.py:14
@@ -991,12 +991,12 @@ msgstr "Perfil Cura"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:12
msgctxt "@item:inmenu"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Assistente de perfis"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:17
msgctxt "@item:inlistbox"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Assistente de perfis"
#: /home/ruben/Projects/Cura/plugins/3MFWriter/__init__.py:30
msgctxt "@item:inlistbox"
@@ -1197,13 +1197,13 @@ msgstr "Falha ao importar perfil de {0}: {1} or !"
msgid "This profile {0} contains incorrect data, could not import it."
-msgstr ""
+msgstr "O perfil {0} contém dados incorretos, não foi possível importá-lo."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:240
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "The machine defined in profile {0} doesn't match with your current machine, could not import it."
-msgstr ""
+msgstr "A máquina definida no perfil {0} não corresponde à sua máquina atual, não foi possível importá-la."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:274
#, python-brace-format
@@ -1215,7 +1215,7 @@ msgstr "Perfil {0} importado com êxito"
#, python-brace-format
msgctxt "@info:status"
msgid "File {0} does not contain any valid profile."
-msgstr ""
+msgstr "O ficheiro {0} não contém nenhum perfil válido."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:280
#, python-brace-format
@@ -1243,7 +1243,7 @@ msgstr "Não foi possível encontrar um tipo de qualidade {0} para a configuraç
#, python-brace-format
msgctxt "@label"
msgid "Group #{group_nr}"
-msgstr ""
+msgstr "Grupo #{group_nr}"
#: /home/ruben/Projects/Cura/cura/BuildVolume.py:100
msgctxt "@info:status"
@@ -1303,7 +1303,7 @@ msgid ""
"
A fatal error has occurred. Please send us this Crash Report to fix the problem
\n"
"
Please use the \"Send report\" button to post a bug report automatically to our servers
\n"
" "
-msgstr ""
+msgstr "
Ocorreu um erro fatal. Envie-nos este relatório de falhas para resolver o problema
\n
Utilize o botão \"Enviar relatório\" para publicar um relatório de erros automaticamente nos nossos servidores
"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:147
msgctxt "@title:groupbox"
msgid "Error traceback"
-msgstr ""
+msgstr "Determinação da origem do erro"
# rever!
# Registos?
@@ -1579,7 +1579,7 @@ msgstr "Tamanho do nozzle"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:393
msgctxt "@label"
msgid "Compatible material diameter"
-msgstr ""
+msgstr "Diâmetro do material compatível"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:395
msgctxt "@tooltip"
@@ -1681,10 +1681,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 diretamente para a sua impressora através da rede, certifique-se de que a sua impressora está ligada à rede por meio de um cabo de rede ou através de ligação à rede Wi-Fi. Se não ligar o Cura à impressora, poderá ainda assim utilizar uma unidade USB para transferir ficheiros g-code para a impressora.\n"
-"\n"
-"Selecione a sua impressora na lista a seguir:"
+msgstr "Para imprimir diretamente para a sua impressora através da rede, certifique-se de que a sua impressora está ligada à rede por meio de um cabo de rede ou através de ligação à rede Wi-Fi. Se não ligar o Cura à impressora, poderá ainda assim utilizar uma unidade USB para transferir ficheiros g-code para a impressora.\n\nSelecione a sua impressora na lista a seguir:"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:75
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:44
@@ -1724,12 +1721,12 @@ msgstr "Tipo"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:233
msgctxt "@label Printer name"
msgid "Ultimaker 3"
-msgstr ""
+msgstr "Ultimaker 3"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:236
msgctxt "@label Printer name"
msgid "Ultimaker 3 Extended"
-msgstr ""
+msgstr "Ultimaker 3 Extended"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:252
msgctxt "@label"
@@ -1796,7 +1793,7 @@ msgstr "%1 não está configurado para alojar um grupo de impressoras Ultimaker
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml:55
msgctxt "@label link to connect manager"
msgid "Add/Remove printers"
-msgstr ""
+msgstr "Adicionar/remover impressoras"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/OpenPanelButton.qml:14
msgctxt "@info:tooltip"
@@ -1836,7 +1833,7 @@ msgstr "Perdeu-se a ligação com a impressora"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:47
msgctxt "@label Printer status"
msgid "Unknown"
-msgstr ""
+msgstr "Desconhecido"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:257
msgctxt "@label:status"
@@ -1932,164 +1929,164 @@ msgstr "Ativar Configuração"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:21
msgctxt "@title:window"
msgid "SolidWorks: Export wizard"
-msgstr ""
+msgstr "SolidWorks: Exportar assistente"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:45
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:140
msgctxt "@action:label"
msgid "Quality:"
-msgstr ""
+msgstr "Qualidade:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:78
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:179
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (3D-printing)"
-msgstr ""
+msgstr "Alta resolução (impressão 3D)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:79
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:180
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (3D-printing)"
-msgstr ""
+msgstr "Baixa resolução (impressão 3D)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:80
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:181
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (SolidWorks)"
-msgstr ""
+msgstr "Alta resolução (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:81
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:182
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (SolidWorks)"
-msgstr ""
+msgstr "Baixa resolução (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:94
msgctxt "@text:window"
msgid "Show this dialog again"
-msgstr ""
+msgstr "Mostrar esta caixa de diálogo novamente"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:104
msgctxt "@action:button"
msgid "Continue"
-msgstr ""
+msgstr "Continuar"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:116
msgctxt "@action:button"
msgid "Abort"
-msgstr ""
+msgstr "Cancelar"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:21
msgctxt "@title:window"
msgid "How to install Cura SolidWorks macro"
-msgstr ""
+msgstr "Como instalar a macro Cura SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:62
msgctxt "@description:label"
msgid "Steps:"
-msgstr ""
+msgstr "Passos:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:140
msgctxt "@action:button"
msgid ""
"Open the directory\n"
"with macro and icon"
-msgstr ""
+msgstr "Abrir o diretório\ncom macro e ícone"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:160
msgctxt "@description:label"
msgid "Instructions:"
-msgstr ""
+msgstr "Instruções:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:202
msgctxt "@action:playpause"
msgid "Play"
-msgstr ""
+msgstr "Reproduzir"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:206
msgctxt "@action:playpause"
msgid "Pause"
-msgstr ""
+msgstr "Colocar em pausa"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:268
msgctxt "@action:button"
msgid "Previous Step"
-msgstr ""
+msgstr "Passo anterior"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:283
msgctxt "@action:button"
msgid "Done"
-msgstr ""
+msgstr "Concluído"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:287
msgctxt "@action:button"
msgid "Next Step"
-msgstr ""
+msgstr "Passo seguinte"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:21
msgctxt "@title:window"
msgid "SolidWorks plugin: Configuration"
-msgstr ""
+msgstr "Plug-in SolidWorks: Configuração"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:39
msgctxt "@title:tab"
msgid "Conversion settings"
-msgstr ""
+msgstr "Definições de conversão"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:66
msgctxt "@label"
msgid "First choice:"
-msgstr ""
+msgstr "Primeira escolha:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:86
msgctxt "@text:menu"
msgid "Latest installed version (Recommended)"
-msgstr ""
+msgstr "Última versão instalada (Recomendada)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:95
msgctxt "@text:menu"
msgid "Default version"
-msgstr ""
+msgstr "Versão predefinida"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:193
msgctxt "@label"
msgid "Show wizard before opening SolidWorks files"
-msgstr ""
+msgstr "Mostrar assistente antes de abrir ficheiros SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:203
msgctxt "@label"
msgid "Automatically rotate opened file into normed orientation"
-msgstr ""
+msgstr "Rodar automaticamente o ficheiro aberto para uma orientação normalizada"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:210
msgctxt "@title:tab"
msgid "Installation(s)"
-msgstr ""
+msgstr "Instalação(ões)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:284
msgctxt "@label"
msgid "COM service found"
-msgstr ""
+msgstr "Serviço COM encontrado"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:295
msgctxt "@label"
msgid "Executable found"
-msgstr ""
+msgstr "Executável encontrado"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:306
msgctxt "@label"
msgid "COM starting"
-msgstr ""
+msgstr "A iniciar COM"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:317
msgctxt "@label"
msgid "Revision number"
-msgstr ""
+msgstr "Número da revisão"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:328
msgctxt "@label"
msgid "Functions available"
-msgstr ""
+msgstr "Funções disponíveis"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:341
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:264
@@ -2282,32 +2279,32 @@ msgstr "Suavização"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:38
msgctxt "@label"
msgid "Mesh Type"
-msgstr ""
+msgstr "Tipo de malha"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:69
msgctxt "@label"
msgid "Normal model"
-msgstr ""
+msgstr "Modelo normal"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:76
msgctxt "@label"
msgid "Print as support"
-msgstr ""
+msgstr "Imprimir como suporte"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:84
msgctxt "@label"
msgid "Don't support overlap with other models"
-msgstr ""
+msgstr "Não suportar sobreposição com outros modelos"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:92
msgctxt "@label"
msgid "Modify settings for overlap with other models"
-msgstr ""
+msgstr "Modificar definições para sobreposição com outros modelos"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:100
msgctxt "@label"
msgid "Modify settings for infill of other models"
-msgstr ""
+msgstr "Modificar definições para preenchimento de outros modelos"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:333
msgctxt "@action:button"
@@ -2495,10 +2492,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 plug-in contém uma licença.\n"
-"É necessário aceitar esta licença para instalar o plug-in.\n"
-"Concorda com os termos abaixo?"
+msgstr "Este plug-in contém uma licença.\nÉ necessário aceitar esta licença para instalar o plug-in.\nConcorda com os termos abaixo?"
#: /home/ruben/Projects/Cura/plugins/PluginBrowser/PluginBrowser.qml:242
msgctxt "@action:button"
@@ -2771,9 +2765,7 @@ msgctxt "@text:window"
msgid ""
"You have customized some profile settings.\n"
"Would you like to keep or discard those settings?"
-msgstr ""
-"Alterou algumas das definições do perfil.\n"
-"Gostaria de manter ou descartar essas alterações?"
+msgstr "Alterou algumas das definições do perfil.\nGostaria de manter ou descartar essas alterações?"
#: /home/ruben/Projects/Cura/resources/qml/DiscardOrKeepProfileChangesDialog.qml:110
msgctxt "@title:column"
@@ -3181,27 +3173,27 @@ msgstr "Enviar informações (anónimas) da impressão"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:674
msgctxt "@label"
msgid "Experimental"
-msgstr ""
+msgstr "Experimental"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:680
msgctxt "@info:tooltip"
msgid "Use multi build plate functionality"
-msgstr ""
+msgstr "Utilizar a funcionalidade de múltiplas placas de construção"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:685
msgctxt "@option:check"
msgid "Use multi build plate functionality (restart required)"
-msgstr ""
+msgstr "Utilizar a funcionalidade de múltiplas placas de construção (é necessário reiniciar)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:694
msgctxt "@info:tooltip"
msgid "Should newly loaded models be arranged on the build plate? Used in conjunction with multi build plate (EXPERIMENTAL)"
-msgstr ""
+msgstr "Os modelos recém-carregados devem ser dispostos na placa de construção? Utilizado em conjunto com múltiplas placas de construção (EXPERIMENTAL)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:699
msgctxt "@option:check"
msgid "Do not arrange objects on load"
-msgstr ""
+msgstr "Não dispor objetos durante o carregamento"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:15
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:514
@@ -3445,9 +3437,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 ""
-"O Cura foi desenvolvido pela Ultimaker B.V. em colaboração com a comunidade.\n"
-"O Cura tem o prazer de utilizar os seguintes projetos open source:"
+msgstr "O Cura foi desenvolvido pela Ultimaker B.V. em colaboração com a comunidade.\nO Cura tem o prazer de utilizar os seguintes projetos open source:"
#: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:118
msgctxt "@label"
@@ -3557,10 +3547,7 @@ msgid ""
"Some setting/override values are different from the values stored in the profile.\n"
"\n"
"Click to open the profile manager."
-msgstr ""
-"Alguns valores de definição/substituição são diferentes dos valores armazenados no perfil.\n"
-"\n"
-"Clique para abrir o gestor de perfis."
+msgstr "Alguns valores de definição/substituição são diferentes dos valores armazenados no perfil.\n\nClique para abrir o gestor de perfis."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:150
msgctxt "@label:textbox"
@@ -3602,10 +3589,7 @@ msgid ""
"Some hidden settings use values different from their normal calculated value.\n"
"\n"
"Click to make these settings visible."
-msgstr ""
-"Algumas das definições invisíveis têm valores diferentes dos valores normais calculados automaticamente.\n"
-"\n"
-"Clique para tornar estas definições visíveis."
+msgstr "Algumas das definições invisíveis têm valores diferentes dos valores normais calculados automaticamente.\n\nClique para tornar estas definições visíveis."
# rever!
# Afeta?
@@ -3625,7 +3609,7 @@ msgstr "Modificado Por"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:156
msgctxt "@label"
msgid "This setting is always shared between all extruders. Changing it here will change the value for all extruders."
-msgstr ""
+msgstr "Esta definição é sempre partilhada entre todas as extrusoras. Ao alterá-la aqui, o valor será alterado para todas as extrusoras."
# rever!
# contexto?!
@@ -3642,10 +3626,7 @@ msgid ""
"This setting has a value that is different from the profile.\n"
"\n"
"Click to restore the value of the profile."
-msgstr ""
-"Esta definição tem um valor que é diferente do perfil.\n"
-"\n"
-"Clique para restaurar o valor do perfil."
+msgstr "Esta definição tem um valor que é diferente do perfil.\n\nClique para restaurar o valor do perfil."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:288
msgctxt "@label"
@@ -3653,10 +3634,7 @@ msgid ""
"This setting is normally calculated, but it currently has an absolute value set.\n"
"\n"
"Click to restore the calculated value."
-msgstr ""
-"Normalmente, o valor desta definição é calculado, mas atualmente tem definido um valor absoluto.\n"
-"\n"
-"Clique para restaurar o valor calculado."
+msgstr "Normalmente, o valor desta definição é calculado, mas atualmente tem definido um valor absoluto.\n\nClique para restaurar o valor calculado."
# rever!
# Configuração da Impressão?
@@ -3670,9 +3648,7 @@ msgctxt "@label:listbox"
msgid ""
"Print Setup disabled\n"
"G-code files cannot be modified"
-msgstr ""
-"Configuração da Impressão desativada\n"
-"Os ficheiros G-code não podem ser modificados"
+msgstr "Configuração da Impressão desativada\nOs ficheiros G-code não podem ser modificados"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:342
msgctxt "@label Hours and minutes"
@@ -3682,7 +3658,7 @@ msgstr "00h00min"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:359
msgctxt "@tooltip"
msgid "Time specification"
-msgstr ""
+msgstr "Especificação de tempo"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:441
msgctxt "@label"
@@ -3739,12 +3715,12 @@ msgstr "&Visualizar"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:37
msgctxt "@action:inmenu menubar:view"
msgid "&Camera position"
-msgstr ""
+msgstr "&Posição da câmara"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:52
msgctxt "@action:inmenu menubar:view"
msgid "&Build plate"
-msgstr ""
+msgstr "&Placa de construção"
#: /home/ruben/Projects/Cura/resources/qml/Menus/NozzleMenu.qml:40
msgctxt "@title:menuitem %1 is the nozzle currently loaded in the printer"
@@ -3920,27 +3896,27 @@ msgstr "&Sair"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:114
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
-msgstr ""
+msgstr "&Visualização 3D"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:121
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
-msgstr ""
+msgstr "&Vista frontal"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:128
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
-msgstr ""
+msgstr "&Vista superior"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:135
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
-msgstr ""
+msgstr "&Vista lateral esquerda"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:142
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
-msgstr ""
+msgstr "&Vista lateral direita"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:149
msgctxt "@action:inmenu"
@@ -4066,7 +4042,7 @@ msgstr "Re&carregar todos os modelos"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:edit"
msgid "Arrange All Models To All Build Plates"
-msgstr ""
+msgstr "Dispor todos os modelos para todas as placas de construção"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:358
msgctxt "@action:inmenu menubar:edit"
@@ -4128,7 +4104,7 @@ msgstr "Plug-ins instalados..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:438
msgctxt "@action:inmenu menubar:view"
msgid "Expand/Collapse Sidebar"
-msgstr ""
+msgstr "Expandir/fechar barra lateral"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:26
msgctxt "@label:PrintjobStatus"
@@ -4169,12 +4145,12 @@ msgstr "Seccionamento indisponível"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Slice current printjob"
-msgstr ""
+msgstr "Seccionar o trabalho de impressão atual"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Cancel slicing process"
-msgstr ""
+msgstr "Cancelar o processo de seccionamento"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:183
msgctxt "@label:Printjob"
@@ -4230,7 +4206,7 @@ msgstr "Guardar &como..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:139
msgctxt "@title:menu menubar:file"
msgid "Save &Project..."
-msgstr ""
+msgstr "Guardar &projeto..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:162
msgctxt "@title:menu menubar:toplevel"
@@ -4482,7 +4458,7 @@ msgstr "Material"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:352
msgctxt "@label"
msgid "Check compatibility"
-msgstr ""
+msgstr "Verificar a compatibilidade"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:372
msgctxt "@tooltip"
@@ -4492,17 +4468,17 @@ msgstr "Clique para verificar a compatibilidade dos materiais em Ultimaker.com."
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:211
msgctxt "@option:check"
msgid "See only current build plate"
-msgstr ""
+msgstr "Ver apenas a placa de construção atual"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:227
msgctxt "@action:button"
msgid "Arrange to all build plates"
-msgstr ""
+msgstr "Dispor em todas as placas de construção"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:247
msgctxt "@action:button"
msgid "Arrange current build plate"
-msgstr ""
+msgstr "Dispor a placa de construção atual"
#: MachineSettingsAction/plugin.json
msgctxt "description"
@@ -4600,22 +4576,22 @@ msgstr "Impressão através de USB"
#: PrepareStage/plugin.json
msgctxt "description"
msgid "Provides a prepare stage in Cura."
-msgstr ""
+msgstr "Fornece uma fase de preparação no Cura."
#: PrepareStage/plugin.json
msgctxt "name"
msgid "Prepare Stage"
-msgstr ""
+msgstr "Fase de preparação"
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "description"
msgid "Provides an edit window for direct script editing."
-msgstr ""
+msgstr "Fornece uma janela de edição para a edição direta de scripts."
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "name"
msgid "Live scripting tool"
-msgstr ""
+msgstr "Ferramenta de scripting em direto"
#: RemovableDriveOutputDevice/plugin.json
msgctxt "description"
@@ -4640,12 +4616,12 @@ msgstr "Ligação de rede UM3"
#: MonitorStage/plugin.json
msgctxt "description"
msgid "Provides a monitor stage in Cura."
-msgstr ""
+msgstr "Fornece uma fase de monitorização no Cura."
#: MonitorStage/plugin.json
msgctxt "name"
msgid "Monitor Stage"
-msgstr ""
+msgstr "Fase de monitorização"
#: FirmwareUpdateChecker/plugin.json
msgctxt "description"
@@ -4660,7 +4636,7 @@ msgstr "Verificador Atualizações Firmware"
#: CuraSolidWorksPlugin/plugin.json
msgctxt "description"
msgid "Gives you the possibility to open certain files using SolidWorks itself. Conversion is done by this plugin and additional optimizations."
-msgstr ""
+msgstr "Oferece a possibilidade de abrir determinados ficheiros utilizando o SolidWorks. A conversão é efetuada através deste plug-in e de otimizações adicionais."
#: CuraSolidWorksPlugin/plugin.json
msgctxt "name"
@@ -4731,12 +4707,12 @@ msgstr "Leitor de perfis antigos do Cura"
#: CuraBlenderPlugin/plugin.json
msgctxt "description"
msgid "Helps to open Blender files directly in Cura."
-msgstr ""
+msgstr "Ajuda a abrir ficheiros Blender diretamente no Cura."
#: CuraBlenderPlugin/plugin.json
msgctxt "name"
msgid "Blender Integration (experimental)"
-msgstr ""
+msgstr "Integração do Blender (experimental)"
#: GCodeProfileReader/plugin.json
msgctxt "description"
@@ -4903,12 +4879,12 @@ msgstr "Gravador de perfis Cura"
#: CuraPrintProfileCreator/plugin.json
msgctxt "description"
msgid "Allows material manufacturers to create new material and quality profiles using a drop-in UI."
-msgstr ""
+msgstr "Permite aos fabricantes de material a criação de novo material e de perfis de qualidade utilizando uma IU de fácil acesso."
#: CuraPrintProfileCreator/plugin.json
msgctxt "name"
msgid "Print Profile Assistant"
-msgstr ""
+msgstr "Assistente de perfis de impressão"
#: 3MFWriter/plugin.json
msgctxt "description"
diff --git a/resources/i18n/pt_PT/fdmprinter.def.json.po b/resources/i18n/pt_PT/fdmprinter.def.json.po
index 8340465a37..e106780669 100644
--- a/resources/i18n/pt_PT/fdmprinter.def.json.po
+++ b/resources/i18n/pt_PT/fdmprinter.def.json.po
@@ -59,9 +59,7 @@ msgctxt "machine_start_gcode description"
msgid ""
"Gcode commands to be executed at the very start - separated by \n"
"."
-msgstr ""
-"Comandos Gcode a serem executados no início – separados por \n"
-"."
+msgstr "Comandos Gcode a serem executados no início – separados por \n."
#: fdmprinter.def.json
msgctxt "machine_end_gcode label"
@@ -73,9 +71,7 @@ msgctxt "machine_end_gcode description"
msgid ""
"Gcode commands to be executed at the very end - separated by \n"
"."
-msgstr ""
-"Comandos Gcode a serem executados no fim – separados por \n"
-"."
+msgstr "Comandos Gcode a serem executados no fim – separados por \n."
#: fdmprinter.def.json
msgctxt "material_guid label"
@@ -360,12 +356,12 @@ msgstr "Repetier"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract label"
msgid "Firmware Retraction"
-msgstr ""
+msgstr "Retração de firmware"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract description"
msgid "Whether to use firmware retract commands (G10/G11) instead of using the E property in G1 commands to retract the material."
-msgstr ""
+msgstr "Utilizar ou não comandos de retração de firmware (G10/G11) em vez de utilizar a propriedade E em comandos G1 para retrair o material."
#: fdmprinter.def.json
msgctxt "machine_disallowed_areas label"
@@ -1083,12 +1079,12 @@ msgstr "Em todo o lado"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps label"
msgid "Filter Out Tiny Gaps"
-msgstr ""
+msgstr "Eliminar pequenas folgas"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps description"
msgid "Filter out tiny gaps to reduce blobs on outside of model."
-msgstr ""
+msgstr "Eliminar pequenas folgas para reduzir blobs no exterior do modelo."
#: fdmprinter.def.json
msgctxt "fill_outline_gaps label"
@@ -1108,9 +1104,7 @@ msgstr "Expansão Horizontal"
#: fdmprinter.def.json
msgctxt "xy_offset description"
msgid "Amount of offset applied to all polygons in each layer. Positive values can compensate for too big holes; negative values can compensate for too small holes."
-msgstr ""
-"Quantidade de desvio aplicado a todos os polígonos em cada camada.\n"
-" Valores positivos podem compensar buracos demasiado grandes; os valores negativos podem compensar buracos demasiado pequenos."
+msgstr "Quantidade de desvio aplicado a todos os polígonos em cada camada.\n Valores positivos podem compensar buracos demasiado grandes; os valores negativos podem compensar buracos demasiado pequenos."
#: fdmprinter.def.json
msgctxt "xy_offset_layer_0 label"
@@ -1122,9 +1116,7 @@ msgstr "Expansão Horizontal Camada Inicial"
#: fdmprinter.def.json
msgctxt "xy_offset_layer_0 description"
msgid "Amount of offset applied to all polygons in the first layer. A negative value can compensate for squishing of the first layer known as \"elephant's foot\"."
-msgstr ""
-"Quantidade de desvio aplicado a todos os polígonos na primeira camada.\n"
-" Um valor negativo pode compensar o \"esmagamento\" da camada inicial, conhecido como \"pé de elefante\"."
+msgstr "Quantidade de desvio aplicado a todos os polígonos na primeira camada.\n Um valor negativo pode compensar o \"esmagamento\" da camada inicial, conhecido como \"pé de elefante\"."
#: fdmprinter.def.json
msgctxt "z_seam_type label"
@@ -1135,10 +1127,7 @@ msgstr "Alinhamento da Junta-Z"
#: fdmprinter.def.json
msgctxt "z_seam_type description"
msgid "Starting point of each path in a layer. When paths in consecutive layers start at the same point a vertical seam may show on the print. When aligning these near a user specified location, the seam is easiest to remove. When placed randomly the inaccuracies at the paths' start will be less noticeable. When taking the shortest path the print will be quicker."
-msgstr ""
-"Ponto inicial de cada trajetória de uma camada.\n"
-"Quando as trajetórias em camadas consecutivas começam no mesmo ponto, pode aparecer uma junta vertical na impressão.\n"
-" Ao alinhar o inicio das trajectórias próximo a uma posição definida pelo utilizador, é mais fácil remover a linha de junta. Quando dispostas aleatoriamente, as imprecisões no início das trajetórias serão menos perceptíveis. Ao adoptar a trajetória mais curta, a impressão será mais rápida."
+msgstr "Ponto inicial de cada trajetória de uma camada.\nQuando as trajetórias em camadas consecutivas começam no mesmo ponto, pode aparecer uma junta vertical na impressão.\n Ao alinhar o inicio das trajectórias próximo a uma posição definida pelo utilizador, é mais fácil remover a linha de junta. Quando dispostas aleatoriamente, as imprecisões no início das trajetórias serão menos perceptíveis. Ao adoptar a trajetória mais curta, a impressão será mais rápida."
#: fdmprinter.def.json
msgctxt "z_seam_type option back"
@@ -1517,7 +1506,7 @@ msgstr "Deslocar Enchimento em X"
#: fdmprinter.def.json
msgctxt "infill_offset_x description"
msgid "The infill pattern is moved this distance along the X axis."
-msgstr ""
+msgstr "O padrão de preenchimento foi movido a esta distância ao longo do eixo X."
# Desvio?
# Delocar? deslocamento
@@ -1530,7 +1519,7 @@ msgstr "Deslocar Enchimento em Y"
#: fdmprinter.def.json
msgctxt "infill_offset_y description"
msgid "The infill pattern is moved this distance along the Y axis."
-msgstr ""
+msgstr "O padrão de preenchimento foi movido a esta distância ao longo do eixo Y."
#: fdmprinter.def.json
msgctxt "sub_div_rad_add label"
@@ -1550,7 +1539,7 @@ msgstr "Sobreposição Enchimento (%)"
#: fdmprinter.def.json
msgctxt "infill_overlap description"
msgid "The amount of overlap between the infill and the walls as a percentage of the infill line width. A slight overlap allows the walls to connect firmly to the infill."
-msgstr ""
+msgstr "A quantidade de sobreposição entre o preenchimento e as paredes, como percentagem de largura da linha de preenchimento. Uma ligeira sobreposição permite que as paredes sejam ligadas firmemente ao preenchimento."
#: fdmprinter.def.json
msgctxt "infill_overlap_mm label"
@@ -1570,7 +1559,7 @@ msgstr "Sobreposição Revestimento (%)"
#: fdmprinter.def.json
msgctxt "skin_overlap description"
msgid "The amount of overlap between the skin and the walls as a percentage of the skin line width. A slight overlap allows the walls to connect firmly to the skin. This is a percentage of the average line widths of the skin lines and the innermost wall."
-msgstr ""
+msgstr "A quantidade de sobreposição entre o revestimento e as paredes, como percentagem de largura da linha de revestimento. Uma ligeira sobreposição permite que as paredes se liguem firmemente ao revestimento. Esta é uma percentagem das larguras médias de linha das linhas de revestimento e da parede mais interna."
#: fdmprinter.def.json
msgctxt "skin_overlap_mm label"
@@ -1805,7 +1794,7 @@ msgstr "Temperatura da base de construção"
#: fdmprinter.def.json
msgctxt "material_bed_temperature description"
msgid "The temperature used for the heated build plate. If this is 0, the bed temperature will not be adjusted."
-msgstr ""
+msgstr "A temperatura utilizada para a placa de construção aquecida. Se for 0, a temperatura da base não será ajustada."
#: fdmprinter.def.json
msgctxt "material_bed_temperature_layer_0 label"
@@ -3652,9 +3641,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 ""
-"A distância horizontal entre o contorno e o perímetro exterior da primeira camada da impressão.\n"
-"Esta é a distância mínima. Linhas múltiplas de contorno serão impressas para o exterior."
+msgstr "A distância horizontal entre o contorno e o perímetro exterior da primeira camada da impressão.\nEsta é a distância mínima. Linhas múltiplas de contorno serão impressas para o exterior."
#: fdmprinter.def.json
msgctxt "skirt_brim_minimal_length label"
@@ -4449,82 +4436,82 @@ msgstr "experimental!"
#: fdmprinter.def.json
msgctxt "support_tree_enable label"
msgid "Tree Support"
-msgstr ""
+msgstr "Suporte em árvore"
#: fdmprinter.def.json
msgctxt "support_tree_enable description"
msgid "Generate a tree-like support with branches that support your print. This may reduce material usage and print time, but greatly increases slicing time."
-msgstr ""
+msgstr "Crie uma estrutura em forma de árvore com ramos que suportem a sua impressão. Isto poderá reduzir a utilização de material e o tempo de impressão, mas aumentará consideravelmente o tempo de seccionamento."
#: fdmprinter.def.json
msgctxt "support_tree_angle label"
msgid "Tree Support Branch Angle"
-msgstr ""
+msgstr "Ângulo dos ramos do suporte em árvore"
#: fdmprinter.def.json
msgctxt "support_tree_angle description"
msgid "The angle of the branches. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach."
-msgstr ""
+msgstr "O ângulo dos ramos. Utilize um ângulo menor para torná-los mais verticais e estáveis. Utilize um ângulo maior para obter mais alcance."
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance label"
msgid "Tree Support Branch Distance"
-msgstr ""
+msgstr "Distância dos ramos do suporte em árvore"
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance description"
msgid "How far apart the branches need to be when they touch the model. Making this distance small will cause the tree support to touch the model at more points, causing better overhang but making support harder to remove."
-msgstr ""
+msgstr "A distância a que os ramos devem encontrar-se ao entrar em contacto com o modelo. Diminuir esta distância irá fazer com que o suporte em árvore entre em contacto com o modelo em vários pontos, melhorando a qualidade das saliências, mas dificultando a sua remoção."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter label"
msgid "Tree Support Branch Diameter"
-msgstr ""
+msgstr "Diâmetro dos ramos do suporte em árvore"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter description"
msgid "The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this."
-msgstr ""
+msgstr "O diâmetro dos ramos mais finos do suporte em árvore. Os ramos mais espessos são mais robustos. Os ramos tornar-se-ão mais espessos em direção à base."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle label"
msgid "Tree Support Branch Diameter Angle"
-msgstr ""
+msgstr "Ângulo do diâmetro dos ramos do suporte em árvore"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle description"
msgid "The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the tree support."
-msgstr ""
+msgstr "O ângulo do diâmetro dos ramos, à medida que ficam mais espessos em direção à base. Um ângulo de 0 irá causar uma espessura uniforme dos ramos ao longo do seu comprimento. Um ângulo reduzido poderá aumentar a estabilidade do suporte em árvore."
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution label"
msgid "Tree Support Collision Resolution"
-msgstr ""
+msgstr "Resolução de colisão do suporte em árvore"
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution description"
msgid "Resolution to compute collisions with to avoid hitting the model. Setting this lower will produce more accurate trees that fail less often, but increases slicing time dramatically."
-msgstr ""
+msgstr "Resolução do cálculo de colisões para evitar a colisão com o modelo. Uma definição inferior irá produzir árvores mais precisas com menos falhas, mas aumentar drasticamente o tempo de seccionamento."
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness label"
msgid "Tree Support Wall Thickness"
-msgstr ""
+msgstr "Espessura das paredes do suporte em árvore"
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness description"
msgid "The thickness of the walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "A espessura das paredes dos ramos do suporte em árvore. As paredes mais espessas demoram mais tempo a imprimir, mas não caem tão facilmente."
#: fdmprinter.def.json
msgctxt "support_tree_wall_count label"
msgid "Tree Support Wall Line Count"
-msgstr ""
+msgstr "Contagem de linhas das paredes do suporte em árvore"
#: fdmprinter.def.json
msgctxt "support_tree_wall_count description"
msgid "The number of walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "O número de paredes dos ramos do suporte em árvore. As paredes mais espessas demoram mais tempo a imprimir, mas não caem tão facilmente."
#: fdmprinter.def.json
msgctxt "slicing_tolerance label"
@@ -4601,12 +4588,12 @@ msgstr "Uma lista de ângulos (números inteiros) relativos às direções de li
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization label"
msgid "Infill Travel Optimization"
-msgstr ""
+msgstr "Otimização da deslocação de preenchimento"
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization description"
msgid "When enabled, the order in which the infill lines are printed is optimized to reduce the distance travelled. The reduction in travel time achieved very much depends on the model being sliced, infill pattern, density, etc. Note that, for some models that have many small areas of infill, the time to slice the model may be greatly increased."
-msgstr ""
+msgstr "Quando ativada, a ordem pela qual as linhas de preenchimento são impressas é otimizada para reduzir a distância percorrida. A redução do tempo de deslocação alcançado depende em grande medida do modelo a segmentar, do padrão de preenchimento, da densidade etc. Observe que, em alguns modelos com muitas áreas de preenchimento reduzidas, o tempo de segmentação do modelo pode aumentar consideravelmente."
#: fdmprinter.def.json
msgctxt "material_flow_dependent_temperature label"
@@ -5142,9 +5129,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 ""
-"A distância de um movimento ascendente que é extrudido a metade da velocidade.\n"
-"Isto pode causar melhor aderência às camadas anteriores, sendo que o material nessas camadas não é demasiado aquecido. Aplica-se apenas à impressão de fios."
+msgstr "A distância de um movimento ascendente que é extrudido a metade da velocidade.\nIsto pode causar melhor aderência às camadas anteriores, sendo que o material nessas camadas não é demasiado aquecido. Aplica-se apenas à impressão de fios."
#: fdmprinter.def.json
msgctxt "wireframe_top_jump label"
@@ -5254,42 +5239,42 @@ msgstr "Distância entre o nozzle e as linhas horizontais descendentes. Uma maio
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled label"
msgid "Use adaptive layers"
-msgstr ""
+msgstr "Utilizar camadas adaptáveis"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled description"
msgid "Adaptive layers computes the layer heights depending on the shape of the model."
-msgstr ""
+msgstr "As camadas adaptáveis calculam a altura da camada dependendo da forma do modelo."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation label"
msgid "Adaptive layers maximum variation"
-msgstr ""
+msgstr "Variação máxima das camadas adaptáveis"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation description"
msgid "The maximum allowed height different from the base layer height in mm."
-msgstr ""
+msgstr "A altura máxima permitida em comparação com a altura da camada da base em mm."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step label"
msgid "Adaptive layers variation step size"
-msgstr ""
+msgstr "Tamanho da fase de variação das camadas adaptáveis"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step description"
msgid "The difference in height of the next layer height compared to the previous one."
-msgstr ""
+msgstr "A diferença de altura da camada seguinte em comparação com a anterior."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold label"
msgid "Adaptive layers threshold"
-msgstr ""
+msgstr "Limite das camadas adaptáveis"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold description"
msgid "Threshold whether to use a smaller layer or not. This number is compared to the tan of the steepest slope in a layer."
-msgstr ""
+msgstr "Limita ou não a utilização de uma camada mais pequena. Este número é comparado com a tangente da inclinação mais acentuada numa camada."
#: fdmprinter.def.json
msgctxt "command_line_settings label"
diff --git a/resources/i18n/ru_RU/cura.po b/resources/i18n/ru_RU/cura.po
index ff07772555..5b803cc357 100644
--- a/resources/i18n/ru_RU/cura.po
+++ b/resources/i18n/ru_RU/cura.po
@@ -1,16 +1,16 @@
# Cura
-# Copyright (C) 2017 Ultimaker
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
-"PO-Revision-Date: 2017-11-30 13:05+0100\n"
-"Last-Translator: Ruslan Popov \n"
-"Language-Team: Ruslan Popov\n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
+"PO-Revision-Date: 2018-02-05 13:25+0100\n"
+"Last-Translator: Bothof \n"
+"Language-Team: Ruslan Popov , Russian \n"
"Language: ru_RU\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -189,7 +189,7 @@ msgstr "Прошивка принтера"
#: /home/ruben/Projects/Cura/plugins/PrepareStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Prepare"
-msgstr ""
+msgstr "Подготовка"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:23
msgctxt "@action:button Preceded by 'Ready to'."
@@ -562,7 +562,7 @@ msgstr "Открыть интерфейс с заданиями печати в
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:239
msgctxt "@label Printer name"
msgid "Unknown"
-msgstr ""
+msgstr "Неизвестно"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:505
#, python-brace-format
@@ -597,7 +597,7 @@ msgstr "Подключиться через сеть"
#: /home/ruben/Projects/Cura/plugins/MonitorStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Monitor"
-msgstr ""
+msgstr "Монитор"
#: /home/ruben/Projects/Cura/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py:66
#, python-brace-format
@@ -624,7 +624,7 @@ msgstr "Не могу получить информацию об обновле
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:579
msgctxt "@info:status"
msgid "SolidWorks reported errors, while opening your file. We recommend to solve these issues inside SolidWorks itself."
-msgstr ""
+msgstr "При открытии вашего файла поступили сообщения об ошибках от SolidWorks. Рекомендуется устранить данные проблемы непосредственно в SolidWorks."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:591
msgctxt "@info:status"
@@ -632,7 +632,7 @@ msgid ""
"Found no models inside your drawing. Could you please check it's content again and make sure one part or assembly is inside?\n"
"\n"
" Thanks!."
-msgstr ""
+msgstr "В вашем чертеже не обнаружены модели. Проверьте еще раз его содержимое и убедитесь в наличии одной части или сборки.\n\nБлагодарим!"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:595
msgctxt "@info:status"
@@ -640,7 +640,7 @@ msgid ""
"Found more then one part or assembly inside your drawing. We currently only support drawings with exactly one part or assembly inside.\n"
"\n"
"Sorry!"
-msgstr ""
+msgstr "В вашем чертеже обнаружено больше одной части или сборки. В данный момент поддерживаются исключительно чертежи с одной частью или сборкой.\n\nСожалеем!"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:25
msgctxt "@item:inlistbox"
@@ -655,7 +655,7 @@ msgstr "Файл сборки SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:33
msgctxt "@item:inlistbox"
msgid "SolidWorks drawing file"
-msgstr ""
+msgstr "Файл чертежа SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:48
msgctxt "@info:status"
@@ -665,7 +665,7 @@ msgid ""
"\n"
"With kind regards\n"
" - Thomas Karl Pietrowski"
-msgstr ""
+msgstr "Уважаемый клиент!\nМы не обнаружили подходящую установку SolidWorks в вашей системе. Это означает, что ПО SolidWorks не установлено либо у вас нет подходящей лицензии. Убедитесь, что при запуске ПО SolidWorks оно работает надлежащим образом и (или) обратитесь к своим специалистам по ИКТ.\n\nС наилучшими пожеланиями,\n - Томас Карл Петровски (Thomas Karl Pietrowski)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:57
msgctxt "@info:status"
@@ -675,7 +675,7 @@ msgid ""
"\n"
"With kind regards\n"
" - Thomas Karl Pietrowski"
-msgstr ""
+msgstr "Уважаемый клиент!\nВ данный момент этот плагин запущен в операционной системе, отличной от Windows. Плагин функционирует исключительно под управлением ОС Windows с установленным ПО SolidWorks, для которого имеется подходящая лицензия. Установите данный плагин на принтер под управлением Windows с установленным ПО SolidWorks.\n\nС наилучшими пожеланиями,\n - Томас Карл Петровски (Thomas Karl Pietrowski)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:70
msgid "Configure"
@@ -683,7 +683,7 @@ msgstr "Настройка"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:71
msgid "Installation guide for SolidWorks macro"
-msgstr ""
+msgstr "Руководство по установке макроса SolidWorks"
#: /home/ruben/Projects/Cura/plugins/SimulationView/__init__.py:14
msgctxt "@item:inlistbox"
@@ -707,7 +707,7 @@ msgstr "Изменить G-код"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:43
msgctxt "@info"
msgid "Cura collects anonymized usage statistics."
-msgstr ""
+msgstr "Cura собирает анонимизированную статистику об использовании."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:46
msgctxt "@info:title"
@@ -717,22 +717,22 @@ msgstr "Сбор данных"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:48
msgctxt "@action:button"
msgid "Allow"
-msgstr ""
+msgstr "Разрешить"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:49
msgctxt "@action:tooltip"
msgid "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing."
-msgstr ""
+msgstr "Разрешить Cura отправлять анонимизированную статистику об использовании, чтобы помочь назначить приоритеты будущим улучшениям в Cura. Отправлены некоторые ваши настройки и параметры, включая версию Cura и хэш моделей, разделяемых на слои."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:50
msgctxt "@action:button"
msgid "Disable"
-msgstr ""
+msgstr "Отключить"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:51
msgctxt "@action:tooltip"
msgid "Don't allow Cura to send anonymized usage statistics. You can enable it again in the preferences."
-msgstr ""
+msgstr "Запретить Cura отправлять анонимизированную статистику об использовании. Данное разрешение можно повторно активировать в разделе параметров."
#: /home/ruben/Projects/Cura/plugins/LegacyProfileReader/__init__.py:14
msgctxt "@item:inlistbox"
@@ -742,14 +742,14 @@ msgstr "Профили Cura 15.04"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/__init__.py:15
msgctxt "@item:inlistbox"
msgid "Blender file"
-msgstr ""
+msgstr "Файл Blender"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/CadIntegrationUtils/CommonReader.py:199
msgctxt "@info:status"
msgid ""
"Could not export using \"{}\" quality!\n"
"Felt back to \"{}\"."
-msgstr ""
+msgstr "Не удалось выполнить экспорт с использованием качества \"{}\"!\nВыполнен возврат к \"{}\"."
#: /home/ruben/Projects/Cura/plugins/GCodeProfileReader/__init__.py:14
#: /home/ruben/Projects/Cura/plugins/GCodeReader/__init__.py:14
@@ -936,12 +936,12 @@ msgstr "Профиль Cura"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:12
msgctxt "@item:inmenu"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Помощник по профилю"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:17
msgctxt "@item:inlistbox"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Помощник по профилю"
#: /home/ruben/Projects/Cura/plugins/3MFWriter/__init__.py:30
msgctxt "@item:inlistbox"
@@ -1139,13 +1139,13 @@ msgstr "Невозможно импортировать профиль из or !"
msgid "This profile {0} contains incorrect data, could not import it."
-msgstr ""
+msgstr "Данный профиль {0} содержит неверные данные, поэтому его невозможно импортировать."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:240
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "The machine defined in profile {0} doesn't match with your current machine, could not import it."
-msgstr ""
+msgstr "Аппарат, заданный в профиле {0}, не совпадает с вашим текущим аппаратом, поэтому его невозможно импортировать."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:274
#, python-brace-format
@@ -1157,7 +1157,7 @@ msgstr "Успешно импортирован профиль {0}"
#, python-brace-format
msgctxt "@info:status"
msgid "File {0} does not contain any valid profile."
-msgstr ""
+msgstr "В файле {0} нет подходящих профилей."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:280
#, python-brace-format
@@ -1185,7 +1185,7 @@ msgstr "Невозможно найти тип качества {0} для те
#, python-brace-format
msgctxt "@label"
msgid "Group #{group_nr}"
-msgstr ""
+msgstr "Группа #{group_nr}"
#: /home/ruben/Projects/Cura/cura/BuildVolume.py:100
msgctxt "@info:status"
@@ -1244,7 +1244,7 @@ msgid ""
"
A fatal error has occurred. Please send us this Crash Report to fix the problem
\n"
"
Please use the \"Send report\" button to post a bug report automatically to our servers
\n"
" "
-msgstr ""
+msgstr "
Возникла критическая ошибка. Отправьте нам этот отчет о сбое, чтобы мы могли устранить проблему
\n
Нажмите кнопку «Отправить отчёт», чтобы автоматически отправить отчет об ошибке на наши серверы
"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:147
msgctxt "@title:groupbox"
msgid "Error traceback"
-msgstr ""
+msgstr "Обратное отслеживание ошибки"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:214
msgctxt "@title:groupbox"
@@ -1518,7 +1518,7 @@ msgstr "Диаметр сопла"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:393
msgctxt "@label"
msgid "Compatible material diameter"
-msgstr ""
+msgstr "Диаметр совместимого материала"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:395
msgctxt "@tooltip"
@@ -1620,10 +1620,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 ""
-"Для печати на вашем принтере через сеть, пожалуйста, удостоверьтесь, что ваш принтер подключен к сети с помощью кабеля или через WiFi. Если вы не подключили Cura к вашему принтеру, вы по прежнему можете использовать USB флешку для переноса G-Code файлов на ваш принтер.\n"
-"\n"
-"Укажите ваш принтер в списке ниже:"
+msgstr "Для печати на вашем принтере через сеть, пожалуйста, удостоверьтесь, что ваш принтер подключен к сети с помощью кабеля или через WiFi. Если вы не подключили Cura к вашему принтеру, вы по прежнему можете использовать USB флешку для переноса G-Code файлов на ваш принтер.\n\nУкажите ваш принтер в списке ниже:"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:75
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:44
@@ -1663,12 +1660,12 @@ msgstr "Тип"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:233
msgctxt "@label Printer name"
msgid "Ultimaker 3"
-msgstr ""
+msgstr "Ultimaker 3"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:236
msgctxt "@label Printer name"
msgid "Ultimaker 3 Extended"
-msgstr ""
+msgstr "Ultimaker 3 Extended"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:252
msgctxt "@label"
@@ -1735,7 +1732,7 @@ msgstr "%1 не настроен для управления группой по
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml:55
msgctxt "@label link to connect manager"
msgid "Add/Remove printers"
-msgstr ""
+msgstr "Добавить/удалить принтеры"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/OpenPanelButton.qml:14
msgctxt "@info:tooltip"
@@ -1775,7 +1772,7 @@ msgstr "Потеряно соединение с принтером"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:47
msgctxt "@label Printer status"
msgid "Unknown"
-msgstr ""
+msgstr "Неизвестно"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:257
msgctxt "@label:status"
@@ -1871,164 +1868,164 @@ msgstr "Активировать конфигурацию"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:21
msgctxt "@title:window"
msgid "SolidWorks: Export wizard"
-msgstr ""
+msgstr "SolidWorks: Мастер экспорта"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:45
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:140
msgctxt "@action:label"
msgid "Quality:"
-msgstr ""
+msgstr "Качество:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:78
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:179
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (3D-printing)"
-msgstr ""
+msgstr "Точное (3D-печать)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:79
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:180
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (3D-printing)"
-msgstr ""
+msgstr "Грубое (3D-печать)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:80
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:181
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (SolidWorks)"
-msgstr ""
+msgstr "Точное (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:81
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:182
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (SolidWorks)"
-msgstr ""
+msgstr "Грубое (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:94
msgctxt "@text:window"
msgid "Show this dialog again"
-msgstr ""
+msgstr "Показывать это диалоговое окно в дальнейшем"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:104
msgctxt "@action:button"
msgid "Continue"
-msgstr ""
+msgstr "Продолжить"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:116
msgctxt "@action:button"
msgid "Abort"
-msgstr ""
+msgstr "Прервать"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:21
msgctxt "@title:window"
msgid "How to install Cura SolidWorks macro"
-msgstr ""
+msgstr "Порядок установки макроса Cura SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:62
msgctxt "@description:label"
msgid "Steps:"
-msgstr ""
+msgstr "Шаги:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:140
msgctxt "@action:button"
msgid ""
"Open the directory\n"
"with macro and icon"
-msgstr ""
+msgstr "Откройте каталог\nс макросом и значком"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:160
msgctxt "@description:label"
msgid "Instructions:"
-msgstr ""
+msgstr "Инструкции:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:202
msgctxt "@action:playpause"
msgid "Play"
-msgstr ""
+msgstr "Воспроизвести"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:206
msgctxt "@action:playpause"
msgid "Pause"
-msgstr ""
+msgstr "Пауза"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:268
msgctxt "@action:button"
msgid "Previous Step"
-msgstr ""
+msgstr "Предыдущий шаг"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:283
msgctxt "@action:button"
msgid "Done"
-msgstr ""
+msgstr "Готово"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:287
msgctxt "@action:button"
msgid "Next Step"
-msgstr ""
+msgstr "Следующий шаг"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:21
msgctxt "@title:window"
msgid "SolidWorks plugin: Configuration"
-msgstr ""
+msgstr "Плагин SolidWorks: Конфигурация"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:39
msgctxt "@title:tab"
msgid "Conversion settings"
-msgstr ""
+msgstr "Настройки преобразования"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:66
msgctxt "@label"
msgid "First choice:"
-msgstr ""
+msgstr "Первый выбранный вариант:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:86
msgctxt "@text:menu"
msgid "Latest installed version (Recommended)"
-msgstr ""
+msgstr "Последняя установленная версия (рекомендуется)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:95
msgctxt "@text:menu"
msgid "Default version"
-msgstr ""
+msgstr "Версия по умолчанию"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:193
msgctxt "@label"
msgid "Show wizard before opening SolidWorks files"
-msgstr ""
+msgstr "Показывать окно мастера перед открытием файлов SolidWorks"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:203
msgctxt "@label"
msgid "Automatically rotate opened file into normed orientation"
-msgstr ""
+msgstr "Автоматически поворачивать открытый файл с применением нормированной ориентации"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:210
msgctxt "@title:tab"
msgid "Installation(s)"
-msgstr ""
+msgstr "Установка (-и)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:284
msgctxt "@label"
msgid "COM service found"
-msgstr ""
+msgstr "Обнаружена служба COM"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:295
msgctxt "@label"
msgid "Executable found"
-msgstr ""
+msgstr "Обнаружен исполняемый файл"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:306
msgctxt "@label"
msgid "COM starting"
-msgstr ""
+msgstr "COM запускается"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:317
msgctxt "@label"
msgid "Revision number"
-msgstr ""
+msgstr "Номер редакции"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:328
msgctxt "@label"
msgid "Functions available"
-msgstr ""
+msgstr "Доступные функции"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:341
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:264
@@ -2214,32 +2211,32 @@ msgstr "Сглаживание"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:38
msgctxt "@label"
msgid "Mesh Type"
-msgstr ""
+msgstr "Тип объекта"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:69
msgctxt "@label"
msgid "Normal model"
-msgstr ""
+msgstr "Нормальная модель"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:76
msgctxt "@label"
msgid "Print as support"
-msgstr ""
+msgstr "Печать в качестве поддержки"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:84
msgctxt "@label"
msgid "Don't support overlap with other models"
-msgstr ""
+msgstr "Не поддерживать перекрытие с другими моделями"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:92
msgctxt "@label"
msgid "Modify settings for overlap with other models"
-msgstr ""
+msgstr "Изменять настройки для перекрытия с другими моделями"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:100
msgctxt "@label"
msgid "Modify settings for infill of other models"
-msgstr ""
+msgstr "Изменять настройки для заполнения других моделей"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:333
msgctxt "@action:button"
@@ -2427,10 +2424,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 ""
-"Этот плагин содержит лицензию.\n"
-"Чтобы установить этот плагин, необходимо принять условия лицензии.\n"
-"Принять приведенные ниже условия?"
+msgstr "Этот плагин содержит лицензию.\nЧтобы установить этот плагин, необходимо принять условия лицензии.\nПринять приведенные ниже условия?"
#: /home/ruben/Projects/Cura/plugins/PluginBrowser/PluginBrowser.qml:242
msgctxt "@action:button"
@@ -2695,9 +2689,7 @@ msgctxt "@text:window"
msgid ""
"You have customized some profile settings.\n"
"Would you like to keep or discard those settings?"
-msgstr ""
-"Вы изменили некоторые параметры профиля.\n"
-"Желаете сохранить их или вернуть к прежним значениям?"
+msgstr "Вы изменили некоторые параметры профиля.\nЖелаете сохранить их или вернуть к прежним значениям?"
#: /home/ruben/Projects/Cura/resources/qml/DiscardOrKeepProfileChangesDialog.qml:110
msgctxt "@title:column"
@@ -3101,27 +3093,27 @@ msgstr "Отправлять (анонимно) информацию о печа
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:674
msgctxt "@label"
msgid "Experimental"
-msgstr ""
+msgstr "Экспериментальное"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:680
msgctxt "@info:tooltip"
msgid "Use multi build plate functionality"
-msgstr ""
+msgstr "Использовать функционал нескольких рабочих столов"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:685
msgctxt "@option:check"
msgid "Use multi build plate functionality (restart required)"
-msgstr ""
+msgstr "Использовать функционал нескольких рабочих столов (требуется перезапуск)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:694
msgctxt "@info:tooltip"
msgid "Should newly loaded models be arranged on the build plate? Used in conjunction with multi build plate (EXPERIMENTAL)"
-msgstr ""
+msgstr "Должны ли новые загруженные модели выравниваться на рабочем столе? Используется в сочетании с несколькими рабочими столами (ЭКСПЕРИМЕНТАЛЬНАЯ ОПЦИЯ)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:699
msgctxt "@option:check"
msgid "Do not arrange objects on load"
-msgstr ""
+msgstr "Не выравнивать объекты под нагрузкой"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:15
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:514
@@ -3365,9 +3357,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 разработана компанией Ultimaker B.V. совместно с сообществом.\n"
-"Cura использует следующие проекты с открытым исходным кодом:"
+msgstr "Cura разработана компанией Ultimaker B.V. совместно с сообществом.\nCura использует следующие проекты с открытым исходным кодом:"
#: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:118
msgctxt "@label"
@@ -3475,10 +3465,7 @@ msgid ""
"Some setting/override values are different from the values stored in the profile.\n"
"\n"
"Click to open the profile manager."
-msgstr ""
-"Значения некоторых параметров отличаются от значений профиля.\n"
-"\n"
-"Нажмите для открытия менеджера профилей."
+msgstr "Значения некоторых параметров отличаются от значений профиля.\n\nНажмите для открытия менеджера профилей."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:150
msgctxt "@label:textbox"
@@ -3516,10 +3503,7 @@ msgid ""
"Some hidden settings use values different from their normal calculated value.\n"
"\n"
"Click to make these settings visible."
-msgstr ""
-"Некоторые из скрытых параметров используют значения, отличающиеся от их вычисленных значений.\n"
-"\n"
-"Щёлкните. чтобы сделать эти параметры видимыми."
+msgstr "Некоторые из скрытых параметров используют значения, отличающиеся от их вычисленных значений.\n\nЩёлкните. чтобы сделать эти параметры видимыми."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:61
msgctxt "@label Header for list of settings."
@@ -3534,7 +3518,7 @@ msgstr "Зависит от"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:156
msgctxt "@label"
msgid "This setting is always shared between all extruders. Changing it here will change the value for all extruders."
-msgstr ""
+msgstr "Данная настройка всегда используется совместно всеми экструдерами. Изменение данного значения приведет к изменению значения для всех экструдеров."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:159
msgctxt "@label"
@@ -3547,10 +3531,7 @@ msgid ""
"This setting has a value that is different from the profile.\n"
"\n"
"Click to restore the value of the profile."
-msgstr ""
-"Значение этого параметра отличается от значения в профиле.\n"
-"\n"
-"Щёлкните для восстановления значения из профиля."
+msgstr "Значение этого параметра отличается от значения в профиле.\n\nЩёлкните для восстановления значения из профиля."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:288
msgctxt "@label"
@@ -3558,10 +3539,7 @@ msgid ""
"This setting is normally calculated, but it currently has an absolute value set.\n"
"\n"
"Click to restore the calculated value."
-msgstr ""
-"Обычно это значение вычисляется, но в настоящий момент было установлено явно.\n"
-"\n"
-"Щёлкните для восстановления вычисленного значения."
+msgstr "Обычно это значение вычисляется, но в настоящий момент было установлено явно.\n\nЩёлкните для восстановления вычисленного значения."
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:128
msgctxt "@label:listbox"
@@ -3573,9 +3551,7 @@ msgctxt "@label:listbox"
msgid ""
"Print Setup disabled\n"
"G-code files cannot be modified"
-msgstr ""
-"Настройка принтера отключена\n"
-"G-code файлы нельзя изменять"
+msgstr "Настройка принтера отключена\nG-code файлы нельзя изменять"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:342
msgctxt "@label Hours and minutes"
@@ -3585,7 +3561,7 @@ msgstr "00 ч 00 мин"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:359
msgctxt "@tooltip"
msgid "Time specification"
-msgstr ""
+msgstr "Настройка расчета времени"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:441
msgctxt "@label"
@@ -3642,12 +3618,12 @@ msgstr "Вид"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:37
msgctxt "@action:inmenu menubar:view"
msgid "&Camera position"
-msgstr ""
+msgstr "Положение камеры"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:52
msgctxt "@action:inmenu menubar:view"
msgid "&Build plate"
-msgstr ""
+msgstr "Рабочий стол"
#: /home/ruben/Projects/Cura/resources/qml/Menus/NozzleMenu.qml:40
msgctxt "@title:menuitem %1 is the nozzle currently loaded in the printer"
@@ -3819,27 +3795,27 @@ msgstr "Выход"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:114
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
-msgstr ""
+msgstr "Трехмерный вид"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:121
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
-msgstr ""
+msgstr "Вид спереди"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:128
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
-msgstr ""
+msgstr "Вид сверху"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:135
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
-msgstr ""
+msgstr "Вид слева"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:142
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
-msgstr ""
+msgstr "Вид справа"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:149
msgctxt "@action:inmenu"
@@ -3968,7 +3944,7 @@ msgstr "Перезагрузить все модели"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:edit"
msgid "Arrange All Models To All Build Plates"
-msgstr ""
+msgstr "Выровнять все модели по всем рабочим столам"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:358
msgctxt "@action:inmenu menubar:edit"
@@ -4028,7 +4004,7 @@ msgstr "Установленные плагины..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:438
msgctxt "@action:inmenu menubar:view"
msgid "Expand/Collapse Sidebar"
-msgstr ""
+msgstr "Развернуть/свернуть боковую панель"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:26
msgctxt "@label:PrintjobStatus"
@@ -4063,12 +4039,12 @@ msgstr "Нарезка недоступна"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Slice current printjob"
-msgstr ""
+msgstr "Разделить на слои текущее задание на печать"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Cancel slicing process"
-msgstr ""
+msgstr "Отмена разделения на слои"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:183
msgctxt "@label:Printjob"
@@ -4124,7 +4100,7 @@ msgstr "Сохранить как..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:139
msgctxt "@title:menu menubar:file"
msgid "Save &Project..."
-msgstr ""
+msgstr "Сохранить проект..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:162
msgctxt "@title:menu menubar:toplevel"
@@ -4364,7 +4340,7 @@ msgstr "Материал"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:352
msgctxt "@label"
msgid "Check compatibility"
-msgstr ""
+msgstr "Проверить совместимость"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:372
msgctxt "@tooltip"
@@ -4374,17 +4350,17 @@ msgstr "Нажмите для проверки совместимости мат
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:211
msgctxt "@option:check"
msgid "See only current build plate"
-msgstr ""
+msgstr "Показывать только текущий рабочий стол"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:227
msgctxt "@action:button"
msgid "Arrange to all build plates"
-msgstr ""
+msgstr "Выровнять для всех рабочих столов"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:247
msgctxt "@action:button"
msgid "Arrange current build plate"
-msgstr ""
+msgstr "Выровнять текущий рабочий стол"
#: MachineSettingsAction/plugin.json
msgctxt "description"
@@ -4479,22 +4455,22 @@ msgstr "Печать через USB"
#: PrepareStage/plugin.json
msgctxt "description"
msgid "Provides a prepare stage in Cura."
-msgstr ""
+msgstr "Обеспечивает подготовительный этап в Cura."
#: PrepareStage/plugin.json
msgctxt "name"
msgid "Prepare Stage"
-msgstr ""
+msgstr "Подготовительный этап"
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "description"
msgid "Provides an edit window for direct script editing."
-msgstr ""
+msgstr "Открывает окно редактирования для непосредственного редактирования скриптов."
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "name"
msgid "Live scripting tool"
-msgstr ""
+msgstr "Интерактивный инструмент для работы со скриптами"
#: RemovableDriveOutputDevice/plugin.json
msgctxt "description"
@@ -4519,12 +4495,12 @@ msgstr "Соединение с сетью UM3"
#: MonitorStage/plugin.json
msgctxt "description"
msgid "Provides a monitor stage in Cura."
-msgstr ""
+msgstr "Обеспечивает этап мониторинга в Cura."
#: MonitorStage/plugin.json
msgctxt "name"
msgid "Monitor Stage"
-msgstr ""
+msgstr "Этап мониторинга"
#: FirmwareUpdateChecker/plugin.json
msgctxt "description"
@@ -4539,7 +4515,7 @@ msgstr "Проверка обновлений"
#: CuraSolidWorksPlugin/plugin.json
msgctxt "description"
msgid "Gives you the possibility to open certain files using SolidWorks itself. Conversion is done by this plugin and additional optimizations."
-msgstr ""
+msgstr "Позволяет открывать определенные файлы непосредственно с помощью SolidWorks. Преобразование выполняется с помощью данного плагина и дополнительных оптимизаций."
#: CuraSolidWorksPlugin/plugin.json
msgctxt "name"
@@ -4609,12 +4585,12 @@ msgstr "Чтение устаревших профилей Cura"
#: CuraBlenderPlugin/plugin.json
msgctxt "description"
msgid "Helps to open Blender files directly in Cura."
-msgstr ""
+msgstr "Помогает открывать файлы Blender непосредственно в Cura."
#: CuraBlenderPlugin/plugin.json
msgctxt "name"
msgid "Blender Integration (experimental)"
-msgstr ""
+msgstr "Интеграция Blender (экспериментальная опция)"
#: GCodeProfileReader/plugin.json
msgctxt "description"
@@ -4779,12 +4755,12 @@ msgstr "Запись профиля Cura"
#: CuraPrintProfileCreator/plugin.json
msgctxt "description"
msgid "Allows material manufacturers to create new material and quality profiles using a drop-in UI."
-msgstr ""
+msgstr "Позволяет производителям материалов создавать новые профили материалов и качества с помощью дружественного интерфейса."
#: CuraPrintProfileCreator/plugin.json
msgctxt "name"
msgid "Print Profile Assistant"
-msgstr ""
+msgstr "Помощник по профилю печати"
#: 3MFWriter/plugin.json
msgctxt "description"
diff --git a/resources/i18n/ru_RU/fdmprinter.def.json.po b/resources/i18n/ru_RU/fdmprinter.def.json.po
index 72f419ade9..036d0d5edc 100644
--- a/resources/i18n/ru_RU/fdmprinter.def.json.po
+++ b/resources/i18n/ru_RU/fdmprinter.def.json.po
@@ -58,9 +58,7 @@ msgctxt "machine_start_gcode description"
msgid ""
"Gcode commands to be executed at the very start - separated by \n"
"."
-msgstr ""
-"Команды в G-коде, которые будут выполнены при старте печати, разделённые \n"
-"."
+msgstr "Команды в G-коде, которые будут выполнены при старте печати, разделённые \n."
#: fdmprinter.def.json
msgctxt "machine_end_gcode label"
@@ -72,9 +70,7 @@ msgctxt "machine_end_gcode description"
msgid ""
"Gcode commands to be executed at the very end - separated by \n"
"."
-msgstr ""
-"Команды в G-коде, которые будут выполнены в конце печати, разделённые \n"
-"."
+msgstr "Команды в G-коде, которые будут выполнены в конце печати, разделённые \n."
#: fdmprinter.def.json
msgctxt "material_guid label"
@@ -354,12 +350,12 @@ msgstr "Repetier"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract label"
msgid "Firmware Retraction"
-msgstr ""
+msgstr "Откат встроенного программного обеспечения"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract description"
msgid "Whether to use firmware retract commands (G10/G11) instead of using the E property in G1 commands to retract the material."
-msgstr ""
+msgstr "Определяет, использовать ли команды отката встроенного программного обеспечения (G10/G11) вместо применения свойства E в командах G1 для отката материала."
#: fdmprinter.def.json
msgctxt "machine_disallowed_areas label"
@@ -1054,12 +1050,12 @@ msgstr "Везде"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps label"
msgid "Filter Out Tiny Gaps"
-msgstr ""
+msgstr "Фильтровать только небольшие зазоры"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps description"
msgid "Filter out tiny gaps to reduce blobs on outside of model."
-msgstr ""
+msgstr "Фильтровать только небольшие зазоры для сокращения пузырей за пределами модели."
#: fdmprinter.def.json
msgctxt "fill_outline_gaps label"
@@ -1444,7 +1440,7 @@ msgstr "Смещение заполнения по X"
#: fdmprinter.def.json
msgctxt "infill_offset_x description"
msgid "The infill pattern is moved this distance along the X axis."
-msgstr ""
+msgstr "Расстояние перемещения шаблона заполнения по оси X."
#: fdmprinter.def.json
msgctxt "infill_offset_y label"
@@ -1454,7 +1450,7 @@ msgstr "Смещение заполнения по Y"
#: fdmprinter.def.json
msgctxt "infill_offset_y description"
msgid "The infill pattern is moved this distance along the Y axis."
-msgstr ""
+msgstr "Расстояние перемещения шаблона заполнения по оси Y."
#: fdmprinter.def.json
msgctxt "sub_div_rad_add label"
@@ -1474,7 +1470,7 @@ msgstr "Процент перекрытие заполнения"
#: fdmprinter.def.json
msgctxt "infill_overlap description"
msgid "The amount of overlap between the infill and the walls as a percentage of the infill line width. A slight overlap allows the walls to connect firmly to the infill."
-msgstr ""
+msgstr "Величина перекрытия между заполнением и стенками в виде процентного отношения от ширины линии заполнения. Небольшое перекрытие позволяет стенкам надежно соединяться с заполнением."
#: fdmprinter.def.json
msgctxt "infill_overlap_mm label"
@@ -1494,7 +1490,7 @@ msgstr "Процент перекрытия оболочек"
#: fdmprinter.def.json
msgctxt "skin_overlap description"
msgid "The amount of overlap between the skin and the walls as a percentage of the skin line width. A slight overlap allows the walls to connect firmly to the skin. This is a percentage of the average line widths of the skin lines and the innermost wall."
-msgstr ""
+msgstr "Величина перекрытия между оболочкой и стенками в виде процентного отношения от ширины линии оболочки. Небольшое перекрытие позволяет стенкам надежно соединяться с оболочкой. Это значение является процентным отношением от средней ширины линии оболочки и внутренней стенки."
#: fdmprinter.def.json
msgctxt "skin_overlap_mm label"
@@ -1724,7 +1720,7 @@ msgstr "Температура стола"
#: fdmprinter.def.json
msgctxt "material_bed_temperature description"
msgid "The temperature used for the heated build plate. If this is 0, the bed temperature will not be adjusted."
-msgstr ""
+msgstr "Температура, задаваемая для разогретого рабочего стола. Если значение равно 0, температура основания не будет регулироваться."
#: fdmprinter.def.json
msgctxt "material_bed_temperature_layer_0 label"
@@ -3506,9 +3502,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 ""
-"Горизонтальное расстояние между юбкой и первым слоем печати.\n"
-"Минимальное расстояние. Несколько линий юбки будут расширяться от этого расстояния."
+msgstr "Горизонтальное расстояние между юбкой и первым слоем печати.\nМинимальное расстояние. Несколько линий юбки будут расширяться от этого расстояния."
#: fdmprinter.def.json
msgctxt "skirt_brim_minimal_length label"
@@ -4268,82 +4262,82 @@ msgstr "экспериментальное!"
#: fdmprinter.def.json
msgctxt "support_tree_enable label"
msgid "Tree Support"
-msgstr ""
+msgstr "Древовидная поддержка"
#: fdmprinter.def.json
msgctxt "support_tree_enable description"
msgid "Generate a tree-like support with branches that support your print. This may reduce material usage and print time, but greatly increases slicing time."
-msgstr ""
+msgstr "Генерирование древовидной поддержки с ответвлениями, поддерживающими вашу печать. Данная опция может сэкономить материал и сократить время печати, однако при этом существенно возрастает время разделения на слои."
#: fdmprinter.def.json
msgctxt "support_tree_angle label"
msgid "Tree Support Branch Angle"
-msgstr ""
+msgstr "Угол ответвления древовидной поддержки"
#: fdmprinter.def.json
msgctxt "support_tree_angle description"
msgid "The angle of the branches. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach."
-msgstr ""
+msgstr "Угол ответвлений. При указании меньшего угла поддержка будет более вертикальной и устойчивой. Для получения большего охвата указывайте более высокий угол."
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance label"
msgid "Tree Support Branch Distance"
-msgstr ""
+msgstr "Расстояние ответвления древовидной поддержки"
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance description"
msgid "How far apart the branches need to be when they touch the model. Making this distance small will cause the tree support to touch the model at more points, causing better overhang but making support harder to remove."
-msgstr ""
+msgstr "Указывает, насколько далеко должны друг от друга располагаться ответвления при касании модели. Если задать небольшое расстояние, увеличится количество точек, в которых древовидная поддержка касается модели; это улучшает нависание, но при этом усложняет удаление поддержки."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter label"
msgid "Tree Support Branch Diameter"
-msgstr ""
+msgstr "Диаметр ответвления древовидной поддержки"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter description"
msgid "The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this."
-msgstr ""
+msgstr "Диаметр самых тонких ответвлений древовидной поддержки. Чем толще ответвление, тем оно крепче. Ответвления возле основания будут иметь толщину, превышающую данное значение."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle label"
msgid "Tree Support Branch Diameter Angle"
-msgstr ""
+msgstr "Угол диаметра ответвления древовидной поддержки"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle description"
msgid "The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the tree support."
-msgstr ""
+msgstr "Угол диаметра ответвлений по мере их постепенного утолщения к основанию. Если значение угла равно 0, ответвления будут иметь одинаковую толщину по всей своей длине. Небольшой угол может повысить устойчивость древовидной поддержки."
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution label"
msgid "Tree Support Collision Resolution"
-msgstr ""
+msgstr "Разрешение для расчета столкновений древовидной поддержки"
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution description"
msgid "Resolution to compute collisions with to avoid hitting the model. Setting this lower will produce more accurate trees that fail less often, but increases slicing time dramatically."
-msgstr ""
+msgstr "Разрешение, применяемое при расчете столкновений во избежание столкновений с моделью. Если указать меньшее значение, древовидные структуры будут получаться более точными и устойчивыми, однако при этом значительно увеличится время разделения на слои."
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness label"
msgid "Tree Support Wall Thickness"
-msgstr ""
+msgstr "Толщина стенки древовидной поддержки"
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness description"
msgid "The thickness of the walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "Толщина стенок ответвлений древовидной поддержки. Для печати утолщенных стенок требуется больше времени, однако при этом возрастает устойчивость поддержки."
#: fdmprinter.def.json
msgctxt "support_tree_wall_count label"
msgid "Tree Support Wall Line Count"
-msgstr ""
+msgstr "Количество линий стенки древовидной поддержки"
#: fdmprinter.def.json
msgctxt "support_tree_wall_count description"
msgid "The number of walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "Количество стенок ответвлений древовидной поддержки. Для печати утолщенных стенок требуется больше времени, однако при этом возрастает устойчивость поддержки."
#: fdmprinter.def.json
msgctxt "slicing_tolerance label"
@@ -4418,12 +4412,12 @@ msgstr "Список направлений линии при печати сл
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization label"
msgid "Infill Travel Optimization"
-msgstr ""
+msgstr "Оптимизация перемещения заполнения"
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization description"
msgid "When enabled, the order in which the infill lines are printed is optimized to reduce the distance travelled. The reduction in travel time achieved very much depends on the model being sliced, infill pattern, density, etc. Note that, for some models that have many small areas of infill, the time to slice the model may be greatly increased."
-msgstr ""
+msgstr "Если включено, заказ, в котором печатаются линии заполнения, оптимизируется для сокращения пройденного расстояния. Достигнутое сокращение времени перемещения в очень большой степени зависит от модели, разделяемой на слои, шаблона заполнения, плотности и т. п. Обратите внимание, что для некоторых моделей, имеющих множество небольших заполняемых областей, время разделения на слои может существенно возрасти."
#: fdmprinter.def.json
msgctxt "material_flow_dependent_temperature label"
@@ -4945,9 +4939,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 ""
-"Расстояние движения вверх, при котором выдавливание идёт на половине скорости.\n"
-"Это может улучшить прилипание к предыдущим слоям, не перегревая материал тех слоёв. Применяется только при каркасной печати."
+msgstr "Расстояние движения вверх, при котором выдавливание идёт на половине скорости.\nЭто может улучшить прилипание к предыдущим слоям, не перегревая материал тех слоёв. Применяется только при каркасной печати."
#: fdmprinter.def.json
msgctxt "wireframe_top_jump label"
@@ -5057,42 +5049,42 @@ msgstr "Зазор между соплом и горизонтально нис
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled label"
msgid "Use adaptive layers"
-msgstr ""
+msgstr "Использовать адаптивные слои"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled description"
msgid "Adaptive layers computes the layer heights depending on the shape of the model."
-msgstr ""
+msgstr "В случае адаптивных слоев расчет высоты слоя осуществляется в зависимости от формы модели."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation label"
msgid "Adaptive layers maximum variation"
-msgstr ""
+msgstr "Максимальная вариация адаптивных слоев"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation description"
msgid "The maximum allowed height different from the base layer height in mm."
-msgstr ""
+msgstr "Максимальная разрешенная высота по сравнению с высотой базового уровня (в мм)."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step label"
msgid "Adaptive layers variation step size"
-msgstr ""
+msgstr "Размер шага вариации адаптивных слоев"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step description"
msgid "The difference in height of the next layer height compared to the previous one."
-msgstr ""
+msgstr "Разница между высотой следующего слоя и высотой предыдущего слоя."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold label"
msgid "Adaptive layers threshold"
-msgstr ""
+msgstr "Порог для адаптивных слове"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold description"
msgid "Threshold whether to use a smaller layer or not. This number is compared to the tan of the steepest slope in a layer."
-msgstr ""
+msgstr "Пороговое значение, при достижении которого будет использоваться меньший слой. Это число сравнивается с тангенсом наиболее крутого наклона в слое."
#: fdmprinter.def.json
msgctxt "command_line_settings label"
diff --git a/resources/i18n/tr_TR/cura.po b/resources/i18n/tr_TR/cura.po
index f449a2a44b..55a7f78890 100644
--- a/resources/i18n/tr_TR/cura.po
+++ b/resources/i18n/tr_TR/cura.po
@@ -1,14 +1,14 @@
# Cura
-# Copyright (C) 2017 Ultimaker
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
-"PO-Revision-Date: 2017-11-30 13:05+0100\n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
+"PO-Revision-Date: 2018-02-05 13:25+0100\n"
"Last-Translator: Bothof \n"
"Language-Team: Turkish\n"
"Language: tr_TR\n"
@@ -187,7 +187,7 @@ msgstr "Yazıcı Bellenimi"
#: /home/ruben/Projects/Cura/plugins/PrepareStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Prepare"
-msgstr ""
+msgstr "Hazırla"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:23
msgctxt "@action:button Preceded by 'Ready to'."
@@ -560,7 +560,7 @@ msgstr "Yazdırma işleri arayüzünü tarayıcınızda açar."
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:239
msgctxt "@label Printer name"
msgid "Unknown"
-msgstr ""
+msgstr "Bilinmeyen"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:505
#, python-brace-format
@@ -595,7 +595,7 @@ msgstr "Ağ ile Bağlan"
#: /home/ruben/Projects/Cura/plugins/MonitorStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Monitor"
-msgstr ""
+msgstr "Görüntüle"
#: /home/ruben/Projects/Cura/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py:66
#, python-brace-format
@@ -622,7 +622,7 @@ msgstr "Güncelleme bilgilerine erişilemedi."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:579
msgctxt "@info:status"
msgid "SolidWorks reported errors, while opening your file. We recommend to solve these issues inside SolidWorks itself."
-msgstr ""
+msgstr "Dosyanızı açarken SolidWorks tarafından hata rapor edildi. Bu sorunları SolidWorks’ün içinde çözmenizi öneririz."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:591
msgctxt "@info:status"
@@ -630,7 +630,7 @@ msgid ""
"Found no models inside your drawing. Could you please check it's content again and make sure one part or assembly is inside?\n"
"\n"
" Thanks!."
-msgstr ""
+msgstr "Çiziminizde model bulunamadı. İçeriğini tekrar kontrol edip bir parçanın veya düzeneğin içinde olduğunu teyit edebilir misiniz?\n\n Teşekkürler!."
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:595
msgctxt "@info:status"
@@ -638,7 +638,7 @@ msgid ""
"Found more then one part or assembly inside your drawing. We currently only support drawings with exactly one part or assembly inside.\n"
"\n"
"Sorry!"
-msgstr ""
+msgstr "Çiziminizin içinde birden fazla parça veya düzenek bulundu. Şu anda sadece içerisinde bir parça veya düzenek olan çizimleri desteklemekteyiz.\n\nÜzgünüz!"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:25
msgctxt "@item:inlistbox"
@@ -653,7 +653,7 @@ msgstr "SolidWorks assembly dosyası"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:33
msgctxt "@item:inlistbox"
msgid "SolidWorks drawing file"
-msgstr ""
+msgstr "SolidWorks çizim dosyası"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:48
msgctxt "@info:status"
@@ -663,7 +663,7 @@ msgid ""
"\n"
"With kind regards\n"
" - Thomas Karl Pietrowski"
-msgstr ""
+msgstr "Sayın müşterimiz,\nSisteminizde SolidWorks’ün geçerli bir yüklemesini bulamadık. Ya sisteminizde SolidWorks yüklü değil ya da geçerli bir lisansa sahip değilsiniz. SolidWorks’ü tek başına sorunsuz bir biçimde çalıştırabildiğinizden emin olun ve/veya ICT’niz ile irtibata geçin.\n\nSaygılarımızla\n - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:57
msgctxt "@info:status"
@@ -673,7 +673,7 @@ msgid ""
"\n"
"With kind regards\n"
" - Thomas Karl Pietrowski"
-msgstr ""
+msgstr "Sayın müşterimiz,\nŞu anda bu eklentiyi Windows dışında farklı bir işletim sisteminde kullanmaktasınız. Bu eklenti sadece Windows işletim sisteminde, geçerli bir lisansa sahip, kurulu SolidWorks programıyla çalışır. Lütfen bu eklentiyi SolidWorks’ün kurulu olduğu Windows işletim sistemli bir bilgisayara yükleyin.\n\nSaygılarımızla\n - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:70
msgid "Configure"
@@ -681,7 +681,7 @@ msgstr "Yapılandırma"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:71
msgid "Installation guide for SolidWorks macro"
-msgstr ""
+msgstr "SolidWorks makro kurulum rehberi"
#: /home/ruben/Projects/Cura/plugins/SimulationView/__init__.py:14
msgctxt "@item:inlistbox"
@@ -705,7 +705,7 @@ msgstr "GCode Değiştir"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:43
msgctxt "@info"
msgid "Cura collects anonymized usage statistics."
-msgstr ""
+msgstr "Cura anonimleştirilmiş kullanım istatistikleri toplar."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:46
msgctxt "@info:title"
@@ -715,22 +715,22 @@ msgstr "Veri Toplanıyor"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:48
msgctxt "@action:button"
msgid "Allow"
-msgstr ""
+msgstr "İzin Verme"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:49
msgctxt "@action:tooltip"
msgid "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing."
-msgstr ""
+msgstr "Programın gelecek sürümlerinin iyileştirilmesine yardımcı olmak için Cura’ya anonimleştirilmiş kullanım istatistikleri gönderme izni verin. Tercih ve ayarlarınızın bazıları, Cura sürümü ve dilimlere ayırdığınız modellerin sağlaması gönderilir."
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:50
msgctxt "@action:button"
msgid "Disable"
-msgstr ""
+msgstr "Devre Dışı Bırakma"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:51
msgctxt "@action:tooltip"
msgid "Don't allow Cura to send anonymized usage statistics. You can enable it again in the preferences."
-msgstr ""
+msgstr "Cura’nın anonimleştirilmiş kullanım istatistikleri göndermesine izin vermeyin. Tercihler sekmesinden tekrar açabilirsiniz."
#: /home/ruben/Projects/Cura/plugins/LegacyProfileReader/__init__.py:14
msgctxt "@item:inlistbox"
@@ -740,14 +740,14 @@ msgstr "Cura 15.04 profilleri"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/__init__.py:15
msgctxt "@item:inlistbox"
msgid "Blender file"
-msgstr ""
+msgstr "Blender dosyası"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/CadIntegrationUtils/CommonReader.py:199
msgctxt "@info:status"
msgid ""
"Could not export using \"{}\" quality!\n"
"Felt back to \"{}\"."
-msgstr ""
+msgstr "\"{}\" quality!\nFell back to \"{}\" kullanarak dışarı aktarım yapılamadı."
#: /home/ruben/Projects/Cura/plugins/GCodeProfileReader/__init__.py:14
#: /home/ruben/Projects/Cura/plugins/GCodeReader/__init__.py:14
@@ -934,12 +934,12 @@ msgstr "Cura Profili"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:12
msgctxt "@item:inmenu"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Profil Asistanı"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:17
msgctxt "@item:inlistbox"
msgid "Profile Assistant"
-msgstr ""
+msgstr "Profil Asistanı"
#: /home/ruben/Projects/Cura/plugins/3MFWriter/__init__.py:30
msgctxt "@item:inlistbox"
@@ -1137,13 +1137,13 @@ msgstr "{0} dosyasından profil içe aktarımı başarısı
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "This profile {0} contains incorrect data, could not import it."
-msgstr ""
+msgstr "Bu profil {0} yanlış veri içermekte, içeri aktarılamadı."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:240
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "The machine defined in profile {0} doesn't match with your current machine, could not import it."
-msgstr ""
+msgstr "Profilde {0} tanımlanan mevcut cihazınızla eşleşmemekte, içeri aktarılamadı."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:274
#, python-brace-format
@@ -1155,7 +1155,7 @@ msgstr "Profil başarıyla içe aktarıldı {0}"
#, python-brace-format
msgctxt "@info:status"
msgid "File {0} does not contain any valid profile."
-msgstr ""
+msgstr "Dosya {0} geçerli bir profil içermemekte."
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:280
#, python-brace-format
@@ -1183,7 +1183,7 @@ msgstr "Mevcut yapılandırma için bir kalite tipi {0} bulunamıyor."
#, python-brace-format
msgctxt "@label"
msgid "Group #{group_nr}"
-msgstr ""
+msgstr "Grup #{group_nr}"
#: /home/ruben/Projects/Cura/cura/BuildVolume.py:100
msgctxt "@info:status"
@@ -1242,7 +1242,7 @@ msgid ""
"
A fatal error has occurred. Please send us this Crash Report to fix the problem
\n"
"
Please use the \"Send report\" button to post a bug report automatically to our servers
\n"
" "
-msgstr ""
+msgstr "
Onarılamaz bir hata oluştu. Lütfen sorunu çözmek için bize Çökme Raporunu gönderin
\n
Sunucularımıza otomatik olarak bir hata raporu yüklemek için lütfen \"Rapor gönder\" düğmesini kullanın
"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:147
msgctxt "@title:groupbox"
msgid "Error traceback"
-msgstr ""
+msgstr "Hata geri izleme"
#: /home/ruben/Projects/Cura/cura/CrashHandler.py:214
msgctxt "@title:groupbox"
@@ -1516,7 +1516,7 @@ msgstr "Nozzle boyutu"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:393
msgctxt "@label"
msgid "Compatible material diameter"
-msgstr ""
+msgstr "Uyumlu malzeme çapı"
#: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:395
msgctxt "@tooltip"
@@ -1618,10 +1618,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 ""
-"Yazıcınıza ağ üzerinden doğrudan bağlamak için, lütfen yazıcınızın ağ kablosu kullanan bir ağa bağlı olduğundan emin olun veya yazıcınızı WiFi ağına bağlayın. Cura'ya yazıcınız ile bağlanamıyorsanız g-code dosyalarını yazıcınıza aktarmak için USB sürücüsü kullanabilirsiniz.\n"
-"\n"
-"Aşağıdaki listeden yazıcınızı seçin:"
+msgstr "Yazıcınıza ağ üzerinden doğrudan bağlamak için, lütfen yazıcınızın ağ kablosu kullanan bir ağa bağlı olduğundan emin olun veya yazıcınızı WiFi ağına bağlayın. Cura'ya yazıcınız ile bağlanamıyorsanız g-code dosyalarını yazıcınıza aktarmak için USB sürücüsü kullanabilirsiniz.\n\nAşağıdaki listeden yazıcınızı seçin:"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:75
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:44
@@ -1661,12 +1658,12 @@ msgstr "Tür"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:233
msgctxt "@label Printer name"
msgid "Ultimaker 3"
-msgstr ""
+msgstr "Ultimaker 3"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:236
msgctxt "@label Printer name"
msgid "Ultimaker 3 Extended"
-msgstr ""
+msgstr "Genişletilmiş Ultimaker 3"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:252
msgctxt "@label"
@@ -1733,7 +1730,7 @@ msgstr "%1, bağlı Ultimaker 3 yazıcı grubunu barındırmak için ayarlı de
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml:55
msgctxt "@label link to connect manager"
msgid "Add/Remove printers"
-msgstr ""
+msgstr "Yazıcı Ekle/Kaldır"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/OpenPanelButton.qml:14
msgctxt "@info:tooltip"
@@ -1773,7 +1770,7 @@ msgstr "Yazıcı bağlantısı koptu"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:47
msgctxt "@label Printer status"
msgid "Unknown"
-msgstr ""
+msgstr "Bilinmeyen"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:257
msgctxt "@label:status"
@@ -1869,164 +1866,164 @@ msgstr "Yapılandırmayı Etkinleştir"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:21
msgctxt "@title:window"
msgid "SolidWorks: Export wizard"
-msgstr ""
+msgstr "SolidWorks: Dışarı aktarma sihirbazı"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:45
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:140
msgctxt "@action:label"
msgid "Quality:"
-msgstr ""
+msgstr "Kalite:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:78
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:179
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (3D-printing)"
-msgstr ""
+msgstr "Yüksek Kaliteli (3 boyutlu baskı)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:79
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:180
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (3D-printing)"
-msgstr ""
+msgstr "Yüzeysel (3 boyutlu baskı)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:80
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:181
msgctxt "@option:curaSolidworksStlQuality"
msgid "Fine (SolidWorks)"
-msgstr ""
+msgstr "Yüksek Kaliteli (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:81
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:182
msgctxt "@option:curaSolidworksStlQuality"
msgid "Coarse (SolidWorks)"
-msgstr ""
+msgstr "Yüzeysel (SolidWorks)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:94
msgctxt "@text:window"
msgid "Show this dialog again"
-msgstr ""
+msgstr "Bu diyaloğu tekrardan göster"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:104
msgctxt "@action:button"
msgid "Continue"
-msgstr ""
+msgstr "Devam Et"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksWizard.qml:116
msgctxt "@action:button"
msgid "Abort"
-msgstr ""
+msgstr "Durdur"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:21
msgctxt "@title:window"
msgid "How to install Cura SolidWorks macro"
-msgstr ""
+msgstr "Cura SolidWorks makrosunun yüklenmesi"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:62
msgctxt "@description:label"
msgid "Steps:"
-msgstr ""
+msgstr "Adımlar:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:140
msgctxt "@action:button"
msgid ""
"Open the directory\n"
"with macro and icon"
-msgstr ""
+msgstr "Makro ve simge ile\ndizini açın"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:160
msgctxt "@description:label"
msgid "Instructions:"
-msgstr ""
+msgstr "Talimatlar:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:202
msgctxt "@action:playpause"
msgid "Play"
-msgstr ""
+msgstr "Oynat"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:206
msgctxt "@action:playpause"
msgid "Pause"
-msgstr ""
+msgstr "Duraklat"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:268
msgctxt "@action:button"
msgid "Previous Step"
-msgstr ""
+msgstr "Önceki Adım"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:283
msgctxt "@action:button"
msgid "Done"
-msgstr ""
+msgstr "Bitti"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksMacroTutorial.qml:287
msgctxt "@action:button"
msgid "Next Step"
-msgstr ""
+msgstr "Sonraki Adım"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:21
msgctxt "@title:window"
msgid "SolidWorks plugin: Configuration"
-msgstr ""
+msgstr "SolidWorks eklentisi: Yapılandırma"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:39
msgctxt "@title:tab"
msgid "Conversion settings"
-msgstr ""
+msgstr "Dönüştürme ayarları"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:66
msgctxt "@label"
msgid "First choice:"
-msgstr ""
+msgstr "İlk seçenek:"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:86
msgctxt "@text:menu"
msgid "Latest installed version (Recommended)"
-msgstr ""
+msgstr "Yüklü en son sürüm (Önerilen)"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:95
msgctxt "@text:menu"
msgid "Default version"
-msgstr ""
+msgstr "Varsayılan sürüm"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:193
msgctxt "@label"
msgid "Show wizard before opening SolidWorks files"
-msgstr ""
+msgstr "SolidWorks dosyalarını açmadan önce sihirbazı göster"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:203
msgctxt "@label"
msgid "Automatically rotate opened file into normed orientation"
-msgstr ""
+msgstr "Açılmış dosyayı normlu yöne döndür"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:210
msgctxt "@title:tab"
msgid "Installation(s)"
-msgstr ""
+msgstr "Kurulumlar"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:284
msgctxt "@label"
msgid "COM service found"
-msgstr ""
+msgstr "COM hizmeti bulundu"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:295
msgctxt "@label"
msgid "Executable found"
-msgstr ""
+msgstr "Çalıştırılabilir dosya bulundu"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:306
msgctxt "@label"
msgid "COM starting"
-msgstr ""
+msgstr "COM başlatılıyor"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:317
msgctxt "@label"
msgid "Revision number"
-msgstr ""
+msgstr "Revizyon numarası"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:328
msgctxt "@label"
msgid "Functions available"
-msgstr ""
+msgstr "Mevcut fonksiyonlar"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksConfiguration.qml:341
#: /home/ruben/Projects/Cura/resources/qml/WorkspaceSummaryDialog.qml:264
@@ -2212,32 +2209,32 @@ msgstr "Düzeltme"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:38
msgctxt "@label"
msgid "Mesh Type"
-msgstr ""
+msgstr "Ağ Tipi"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:69
msgctxt "@label"
msgid "Normal model"
-msgstr ""
+msgstr "Normal model"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:76
msgctxt "@label"
msgid "Print as support"
-msgstr ""
+msgstr "Destek olarak yazdır"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:84
msgctxt "@label"
msgid "Don't support overlap with other models"
-msgstr ""
+msgstr "Diğer modellerle örtüşmeyi destekleme"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:92
msgctxt "@label"
msgid "Modify settings for overlap with other models"
-msgstr ""
+msgstr "Diğer modellerle örtüşme ayarlarını değiştir"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:100
msgctxt "@label"
msgid "Modify settings for infill of other models"
-msgstr ""
+msgstr "Diğer modellerle doldurma ayarlarını değiştir"
#: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:333
msgctxt "@action:button"
@@ -2423,10 +2420,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 ""
-"Bu eklenti bir lisans içerir.\n"
-"Bu eklentiyi yüklemek için bu lisansı kabul etmeniz gerekir.\n"
-"Aşağıdaki koşulları kabul ediyor musunuz?"
+msgstr "Bu eklenti bir lisans içerir.\nBu eklentiyi yüklemek için bu lisansı kabul etmeniz gerekir.\nAşağıdaki koşulları kabul ediyor musunuz?"
#: /home/ruben/Projects/Cura/plugins/PluginBrowser/PluginBrowser.qml:242
msgctxt "@action:button"
@@ -2691,9 +2685,7 @@ msgctxt "@text:window"
msgid ""
"You have customized some profile settings.\n"
"Would you like to keep or discard those settings?"
-msgstr ""
-"Bazı profil ayarlarını özelleştirdiniz.\n"
-"Bu ayarları kaydetmek veya iptal etmek ister misiniz?"
+msgstr "Bazı profil ayarlarını özelleştirdiniz.\nBu ayarları kaydetmek veya iptal etmek ister misiniz?"
#: /home/ruben/Projects/Cura/resources/qml/DiscardOrKeepProfileChangesDialog.qml:110
msgctxt "@title:column"
@@ -3097,27 +3089,27 @@ msgstr "(Anonim) yazdırma bilgisi gönder"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:674
msgctxt "@label"
msgid "Experimental"
-msgstr ""
+msgstr "Deneysel"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:680
msgctxt "@info:tooltip"
msgid "Use multi build plate functionality"
-msgstr ""
+msgstr "Çok yapılı levha fonksiyonelliğini kullan"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:685
msgctxt "@option:check"
msgid "Use multi build plate functionality (restart required)"
-msgstr ""
+msgstr "Çok yapılı levha fonksiyonelliğini kullan (yeniden başlatma gerektirir)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:694
msgctxt "@info:tooltip"
msgid "Should newly loaded models be arranged on the build plate? Used in conjunction with multi build plate (EXPERIMENTAL)"
-msgstr ""
+msgstr "Yeni yüklenen modeller yapılı levhaya mı yerleştirilsin? Çok yapılı levha ile birlikte kullanılır (DENEYSEL)"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/GeneralPage.qml:699
msgctxt "@option:check"
msgid "Do not arrange objects on load"
-msgstr ""
+msgstr "Yüklemenin ardından nesneleri yerleştirme"
#: /home/ruben/Projects/Cura/resources/qml/Preferences/MachinesPage.qml:15
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:514
@@ -3361,9 +3353,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, topluluk iş birliği ile Ultimaker B.V. tarafından geliştirilmiştir.\n"
-"Cura aşağıdaki açık kaynak projelerini gururla kullanmaktadır:"
+msgstr "Cura, topluluk iş birliği ile Ultimaker B.V. tarafından geliştirilmiştir.\nCura aşağıdaki açık kaynak projelerini gururla kullanmaktadır:"
#: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:118
msgctxt "@label"
@@ -3471,10 +3461,7 @@ msgid ""
"Some setting/override values are different from the values stored in the profile.\n"
"\n"
"Click to open the profile manager."
-msgstr ""
-"Bazı ayar/geçersiz kılma değerleri profilinizde saklanan değerlerden farklıdır.\n"
-"\n"
-"Profil yöneticisini açmak için tıklayın."
+msgstr "Bazı ayar/geçersiz kılma değerleri profilinizde saklanan değerlerden farklıdır.\n\nProfil yöneticisini açmak için tıklayın."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:150
msgctxt "@label:textbox"
@@ -3512,10 +3499,7 @@ msgid ""
"Some hidden settings use values different from their normal calculated value.\n"
"\n"
"Click to make these settings visible."
-msgstr ""
-"Gizlenen bazı ayarlar normal hesaplanan değerden farklı değerler kullanır.\n"
-"\n"
-"Bu ayarları görmek için tıklayın."
+msgstr "Gizlenen bazı ayarlar normal hesaplanan değerden farklı değerler kullanır.\n\nBu ayarları görmek için tıklayın."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:61
msgctxt "@label Header for list of settings."
@@ -3530,7 +3514,7 @@ msgstr ".........den etkilenir"
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:156
msgctxt "@label"
msgid "This setting is always shared between all extruders. Changing it here will change the value for all extruders."
-msgstr ""
+msgstr "Bu ayar her zaman, tüm ekstrüderler arasında paylaşılır. Buradan değiştirildiğinde tüm ekstrüderler için değer değiştirir."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:159
msgctxt "@label"
@@ -3543,10 +3527,7 @@ msgid ""
"This setting has a value that is different from the profile.\n"
"\n"
"Click to restore the value of the profile."
-msgstr ""
-"Bu ayarın değeri profilden farklıdır.\n"
-"\n"
-"Profil değerini yenilemek için tıklayın."
+msgstr "Bu ayarın değeri profilden farklıdır.\n\nProfil değerini yenilemek için tıklayın."
#: /home/ruben/Projects/Cura/resources/qml/Settings/SettingItem.qml:288
msgctxt "@label"
@@ -3554,10 +3535,7 @@ msgid ""
"This setting is normally calculated, but it currently has an absolute value set.\n"
"\n"
"Click to restore the calculated value."
-msgstr ""
-"Bu ayar normal olarak yapılır ama şu anda mutlak değer ayarı var.\n"
-"\n"
-"Hesaplanan değeri yenilemek için tıklayın."
+msgstr "Bu ayar normal olarak yapılır ama şu anda mutlak değer ayarı var.\n\nHesaplanan değeri yenilemek için tıklayın."
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:128
msgctxt "@label:listbox"
@@ -3569,9 +3547,7 @@ msgctxt "@label:listbox"
msgid ""
"Print Setup disabled\n"
"G-code files cannot be modified"
-msgstr ""
-"Yazdırma Ayarı devre dışı\n"
-"G-code dosyaları üzerinde değişiklik yapılamaz"
+msgstr "Yazdırma Ayarı devre dışı\nG-code dosyaları üzerinde değişiklik yapılamaz"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:342
msgctxt "@label Hours and minutes"
@@ -3581,7 +3557,7 @@ msgstr "00sa 00dk"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:359
msgctxt "@tooltip"
msgid "Time specification"
-msgstr ""
+msgstr "Zaman Özellikleri"
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:441
msgctxt "@label"
@@ -3638,12 +3614,12 @@ msgstr "&Görünüm"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:37
msgctxt "@action:inmenu menubar:view"
msgid "&Camera position"
-msgstr ""
+msgstr "&Kamera konumu"
#: /home/ruben/Projects/Cura/resources/qml/Menus/ViewMenu.qml:52
msgctxt "@action:inmenu menubar:view"
msgid "&Build plate"
-msgstr ""
+msgstr "&Yapı levhası"
#: /home/ruben/Projects/Cura/resources/qml/Menus/NozzleMenu.qml:40
msgctxt "@title:menuitem %1 is the nozzle currently loaded in the printer"
@@ -3813,27 +3789,27 @@ msgstr "&Çıkış"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:114
msgctxt "@action:inmenu menubar:view"
msgid "&3D View"
-msgstr ""
+msgstr "&3 Boyutlu Görünüm"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:121
msgctxt "@action:inmenu menubar:view"
msgid "&Front View"
-msgstr ""
+msgstr "&Önden Görünüm"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:128
msgctxt "@action:inmenu menubar:view"
msgid "&Top View"
-msgstr ""
+msgstr "&Yukarıdan Görünüm"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:135
msgctxt "@action:inmenu menubar:view"
msgid "&Left Side View"
-msgstr ""
+msgstr "&Sol Taraftan Görünüm"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:142
msgctxt "@action:inmenu menubar:view"
msgid "&Right Side View"
-msgstr ""
+msgstr "&Sağ Taraftan Görünüm"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:149
msgctxt "@action:inmenu"
@@ -3959,7 +3935,7 @@ msgstr "Tüm Modelleri Yeniden Yükle"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:351
msgctxt "@action:inmenu menubar:edit"
msgid "Arrange All Models To All Build Plates"
-msgstr ""
+msgstr "Tüm Modelleri Tüm Yapı Levhalarına Yerleştir"
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:358
msgctxt "@action:inmenu menubar:edit"
@@ -4019,7 +3995,7 @@ msgstr "Yüklü eklentiler..."
#: /home/ruben/Projects/Cura/resources/qml/Actions.qml:438
msgctxt "@action:inmenu menubar:view"
msgid "Expand/Collapse Sidebar"
-msgstr ""
+msgstr "Kenar Çubuğunu Genişlet/Daralt"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:26
msgctxt "@label:PrintjobStatus"
@@ -4054,12 +4030,12 @@ msgstr "Dilimleme kullanılamıyor"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Slice current printjob"
-msgstr ""
+msgstr "Mevcut yazdırma görevini dilimlere ayır"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:171
msgctxt "@info:tooltip"
msgid "Cancel slicing process"
-msgstr ""
+msgstr "Dilimleme sürecini iptal et"
#: /home/ruben/Projects/Cura/resources/qml/SaveButton.qml:183
msgctxt "@label:Printjob"
@@ -4115,7 +4091,7 @@ msgstr "&Farklı Kaydet"
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:139
msgctxt "@title:menu menubar:file"
msgid "Save &Project..."
-msgstr ""
+msgstr "Kaydet&Projelendir..."
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:162
msgctxt "@title:menu menubar:toplevel"
@@ -4354,7 +4330,7 @@ msgstr "Malzeme"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:352
msgctxt "@label"
msgid "Check compatibility"
-msgstr ""
+msgstr "Uyumluluğu Kontrol Et"
#: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:372
msgctxt "@tooltip"
@@ -4364,17 +4340,17 @@ msgstr "Malzemenin uyumluluğunu Ultimaker.com üzerinden kontrol etmek için t
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:211
msgctxt "@option:check"
msgid "See only current build plate"
-msgstr ""
+msgstr "Sadece mevcut yapı levhasını görüntüle"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:227
msgctxt "@action:button"
msgid "Arrange to all build plates"
-msgstr ""
+msgstr "Tüm yapı levhalarına yerleştir"
#: /home/ruben/Projects/Cura/resources/qml/ObjectsList.qml:247
msgctxt "@action:button"
msgid "Arrange current build plate"
-msgstr ""
+msgstr "Sadece mevcut yapı levhasına yerleştir"
#: MachineSettingsAction/plugin.json
msgctxt "description"
@@ -4469,22 +4445,22 @@ msgstr "USB yazdırma"
#: PrepareStage/plugin.json
msgctxt "description"
msgid "Provides a prepare stage in Cura."
-msgstr ""
+msgstr "Cura’da hazırlık aşaması sunar."
#: PrepareStage/plugin.json
msgctxt "name"
msgid "Prepare Stage"
-msgstr ""
+msgstr "Hazırlık Aşaması"
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "description"
msgid "Provides an edit window for direct script editing."
-msgstr ""
+msgstr "Doğrudan komut dosyası düzenlemek için düzenleme penceresi sunar."
#: CuraLiveScriptingPlugin/plugin.json
msgctxt "name"
msgid "Live scripting tool"
-msgstr ""
+msgstr "Canlı komut dosyası aracı"
#: RemovableDriveOutputDevice/plugin.json
msgctxt "description"
@@ -4509,12 +4485,12 @@ msgstr "UM3 Ağ Bağlantısı"
#: MonitorStage/plugin.json
msgctxt "description"
msgid "Provides a monitor stage in Cura."
-msgstr ""
+msgstr "Cura’da görüntüleme aşaması sunar."
#: MonitorStage/plugin.json
msgctxt "name"
msgid "Monitor Stage"
-msgstr ""
+msgstr "Görüntüleme Aşaması"
#: FirmwareUpdateChecker/plugin.json
msgctxt "description"
@@ -4529,7 +4505,7 @@ msgstr "Bellenim Güncelleme Denetleyicisi"
#: CuraSolidWorksPlugin/plugin.json
msgctxt "description"
msgid "Gives you the possibility to open certain files using SolidWorks itself. Conversion is done by this plugin and additional optimizations."
-msgstr ""
+msgstr "Bazı dosyaları SolidWorks kullanarak açma imkanı sağlar. Dönüştürme, bu eklenti ve ek optimizasyonlar tarafından gerçekleştirilir."
#: CuraSolidWorksPlugin/plugin.json
msgctxt "name"
@@ -4599,12 +4575,12 @@ msgstr "Eski Cura Profil Okuyucu"
#: CuraBlenderPlugin/plugin.json
msgctxt "description"
msgid "Helps to open Blender files directly in Cura."
-msgstr ""
+msgstr "Cura’da Blender dosyalarını doğrudan açmanıza yardımcı olur."
#: CuraBlenderPlugin/plugin.json
msgctxt "name"
msgid "Blender Integration (experimental)"
-msgstr ""
+msgstr "Blender Entegrasyonu (deneysel)"
#: GCodeProfileReader/plugin.json
msgctxt "description"
@@ -4769,12 +4745,12 @@ msgstr "Cura Profili Yazıcı"
#: CuraPrintProfileCreator/plugin.json
msgctxt "description"
msgid "Allows material manufacturers to create new material and quality profiles using a drop-in UI."
-msgstr ""
+msgstr "Malzeme üreticilerine bir drop-in UI kullanarak yeni malzeme ve kalite profili oluşturma imkanı sunar."
#: CuraPrintProfileCreator/plugin.json
msgctxt "name"
msgid "Print Profile Assistant"
-msgstr ""
+msgstr "Baskı Profili Asistanı"
#: 3MFWriter/plugin.json
msgctxt "description"
diff --git a/resources/i18n/tr_TR/fdmprinter.def.json.po b/resources/i18n/tr_TR/fdmprinter.def.json.po
index ae06ce1095..aba2389cee 100644
--- a/resources/i18n/tr_TR/fdmprinter.def.json.po
+++ b/resources/i18n/tr_TR/fdmprinter.def.json.po
@@ -56,9 +56,7 @@ msgctxt "machine_start_gcode description"
msgid ""
"Gcode commands to be executed at the very start - separated by \n"
"."
-msgstr ""
-"\n"
-" ile ayrılan, başlangıçta yürütülecek G-code komutları."
+msgstr "\n ile ayrılan, başlangıçta yürütülecek G-code komutları."
#: fdmprinter.def.json
msgctxt "machine_end_gcode label"
@@ -70,9 +68,7 @@ msgctxt "machine_end_gcode description"
msgid ""
"Gcode commands to be executed at the very end - separated by \n"
"."
-msgstr ""
-"\n"
-" ile ayrılan, bitişte yürütülecek Gcode komutları."
+msgstr "\n ile ayrılan, bitişte yürütülecek Gcode komutları."
#: fdmprinter.def.json
msgctxt "material_guid label"
@@ -352,12 +348,12 @@ msgstr "Repetier"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract label"
msgid "Firmware Retraction"
-msgstr ""
+msgstr "Üretici Yazılımı Geri Çekme"
#: fdmprinter.def.json
msgctxt "machine_firmware_retract description"
msgid "Whether to use firmware retract commands (G10/G11) instead of using the E property in G1 commands to retract the material."
-msgstr ""
+msgstr "Malzemeyi geri çekmek için G1 komutlarında E özelliği yerine aygıt yazılımı çekme komutlarının (G10/G11) kullanılıp kullanılmayacağı."
#: fdmprinter.def.json
msgctxt "machine_disallowed_areas label"
@@ -1052,12 +1048,12 @@ msgstr "Her bölüm"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps label"
msgid "Filter Out Tiny Gaps"
-msgstr ""
+msgstr "Küçük Boşlukların Filtrelenmesi"
#: fdmprinter.def.json
msgctxt "filter_out_tiny_gaps description"
msgid "Filter out tiny gaps to reduce blobs on outside of model."
-msgstr ""
+msgstr "Modelin dışındaki damlalarını azaltmak için küçük boşlukları filtreleyin."
#: fdmprinter.def.json
msgctxt "fill_outline_gaps label"
@@ -1442,7 +1438,7 @@ msgstr "Dolgu X Kayması"
#: fdmprinter.def.json
msgctxt "infill_offset_x description"
msgid "The infill pattern is moved this distance along the X axis."
-msgstr ""
+msgstr "Dolgu şekli X ekseni boyunca bu mesafe kadar kaydırılır."
#: fdmprinter.def.json
msgctxt "infill_offset_y label"
@@ -1452,7 +1448,7 @@ msgstr "Dolgu Y Kayması"
#: fdmprinter.def.json
msgctxt "infill_offset_y description"
msgid "The infill pattern is moved this distance along the Y axis."
-msgstr ""
+msgstr "Dolgu şekli Y ekseni boyunca bu mesafe kadar kaydırılır."
#: fdmprinter.def.json
msgctxt "sub_div_rad_add label"
@@ -1472,7 +1468,7 @@ msgstr "Dolgu Çakışma Oranı"
#: fdmprinter.def.json
msgctxt "infill_overlap description"
msgid "The amount of overlap between the infill and the walls as a percentage of the infill line width. A slight overlap allows the walls to connect firmly to the infill."
-msgstr ""
+msgstr "Dolgu hattı genişliğinin yüzdesi olarak dolgu ve duvarların arasındaki çakışma miktarı. Ufak bir çakışma duvarların dolguya sıkıca bağlanmasını sağlar."
#: fdmprinter.def.json
msgctxt "infill_overlap_mm label"
@@ -1492,7 +1488,7 @@ msgstr "Yüzey Çakışma Oranı"
#: fdmprinter.def.json
msgctxt "skin_overlap description"
msgid "The amount of overlap between the skin and the walls as a percentage of the skin line width. A slight overlap allows the walls to connect firmly to the skin. This is a percentage of the average line widths of the skin lines and the innermost wall."
-msgstr ""
+msgstr "Yüzey hattı genişliğinin yüzdesi olarak yüzey ve duvar çakışmasının miktarı. Ufak bir çakışma duvarların yüzeye sıkıca bağlanmasını sağlar. Bu, yüzey ve en iç duvar hat eninin ortalama yüzdesidir."
#: fdmprinter.def.json
msgctxt "skin_overlap_mm label"
@@ -1722,7 +1718,7 @@ msgstr "Yapı Levhası Sıcaklığı"
#: fdmprinter.def.json
msgctxt "material_bed_temperature description"
msgid "The temperature used for the heated build plate. If this is 0, the bed temperature will not be adjusted."
-msgstr ""
+msgstr "Isınan yapı levhası için kullanılan sıcaklık. Bu değer 0 ise yatak sıcaklığı değiştirilmez."
#: fdmprinter.def.json
msgctxt "material_bed_temperature_layer_0 label"
@@ -3504,9 +3500,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 ""
-"Baskının eteği ve ilk katmanı arasındaki yatay mesafe.\n"
-"Minimum mesafedir. Bu mesafeden çok sayıda etek hattı dışarı doğru uzanır."
+msgstr "Baskının eteği ve ilk katmanı arasındaki yatay mesafe.\nMinimum mesafedir. Bu mesafeden çok sayıda etek hattı dışarı doğru uzanır."
#: fdmprinter.def.json
msgctxt "skirt_brim_minimal_length label"
@@ -4266,82 +4260,82 @@ msgstr "deneysel!"
#: fdmprinter.def.json
msgctxt "support_tree_enable label"
msgid "Tree Support"
-msgstr ""
+msgstr "Ağaç Destek"
#: fdmprinter.def.json
msgctxt "support_tree_enable description"
msgid "Generate a tree-like support with branches that support your print. This may reduce material usage and print time, but greatly increases slicing time."
-msgstr ""
+msgstr "Baskınızı desteklemesi adına dallarıyla birlikte ağaca benzeyen bir destek oluşturun. Malzeme kullanımı ve baskı süresi düşer ancak dilimleme zamanı da önemli ölçüde artar."
#: fdmprinter.def.json
msgctxt "support_tree_angle label"
msgid "Tree Support Branch Angle"
-msgstr ""
+msgstr "Ağaç Destek Dal Açısı"
#: fdmprinter.def.json
msgctxt "support_tree_angle description"
msgid "The angle of the branches. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach."
-msgstr ""
+msgstr "Dalların açısı. Daha dikey ve daha stabil olmaları için daha düşük bir açı kullanın. Daha fazla erişim için daha yüksek bir açı kullanın."
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance label"
msgid "Tree Support Branch Distance"
-msgstr ""
+msgstr "Ağaç Destek Dal Mesafesi"
#: fdmprinter.def.json
msgctxt "support_tree_branch_distance description"
msgid "How far apart the branches need to be when they touch the model. Making this distance small will cause the tree support to touch the model at more points, causing better overhang but making support harder to remove."
-msgstr ""
+msgstr "Dalların modele temas ettiklerinde birbirlerine ne kadar uzaklıkta olacakları. Bu mesafenin kısa yapılması ağaç desteğin modele daha fazla noktada temas etmesini sağlayarak daha iyi bir sarkma sunacaktır ancak desteğin sökülmesini de daha güç hale getirecektir."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter label"
msgid "Tree Support Branch Diameter"
-msgstr ""
+msgstr "Ağaç Destek Dalının Çapı"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter description"
msgid "The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this."
-msgstr ""
+msgstr "Ağaç desteğin en ince dallarının çapı. Daha kalın dallar daha dayanıklı olur. Tabana doğru uzanan dallar bundan daha kalın olacaktır."
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle label"
msgid "Tree Support Branch Diameter Angle"
-msgstr ""
+msgstr "Ağaç Destek Dalının Çap Açısı"
#: fdmprinter.def.json
msgctxt "support_tree_branch_diameter_angle description"
msgid "The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the tree support."
-msgstr ""
+msgstr "Alta doğru gidildikçe kademeli olarak kalınlaşan dalların açısı. 0 derecelik bir açı dalların uzunluklarını gözetmeksizin tekdüze bir kalınlığa sahip olmalarını sağlayacaktır. Birazcık açı ağaç desteğin sabitliğini artırabilir."
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution label"
msgid "Tree Support Collision Resolution"
-msgstr ""
+msgstr "Ağaç Destek Çarpışma Çözünürlüğü"
#: fdmprinter.def.json
msgctxt "support_tree_collision_resolution description"
msgid "Resolution to compute collisions with to avoid hitting the model. Setting this lower will produce more accurate trees that fail less often, but increases slicing time dramatically."
-msgstr ""
+msgstr "Modele çarpmamak adına çarpışmaları hesaplamak için çözünürlük. Buna düşük bir değerin verilmesi daha az hata çıkaran daha isabetli ağaçların üretilmesini sağlar ancak dilimleme süresini önemli ölçüde artırır."
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness label"
msgid "Tree Support Wall Thickness"
-msgstr ""
+msgstr "Ağaç Destek Duvarının Kalınlığı"
#: fdmprinter.def.json
msgctxt "support_tree_wall_thickness description"
msgid "The thickness of the walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "Ağaç desteğin dallarında yer alan duvarların kalınlığı. Kalın duvarların basılması daha uzun sürer ancak kalın duvarlar kolay devrilmezler."
#: fdmprinter.def.json
msgctxt "support_tree_wall_count label"
msgid "Tree Support Wall Line Count"
-msgstr ""
+msgstr "Ağaç Destek Duvarının Hat Sayısı"
#: fdmprinter.def.json
msgctxt "support_tree_wall_count description"
msgid "The number of walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily."
-msgstr ""
+msgstr "Ağaç desteğin dallarında yer alan duvarların sayısı. Kalın duvarların basılması daha uzun sürer ancak kalın duvarlar kolay devrilmezler."
#: fdmprinter.def.json
msgctxt "slicing_tolerance label"
@@ -4416,12 +4410,12 @@ msgstr "Üst yüzey katmanları çizgi veya zikzak biçimindeyken kullanılacak
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization label"
msgid "Infill Travel Optimization"
-msgstr ""
+msgstr "Dolgu Hareket Optimizasyonu"
#: fdmprinter.def.json
msgctxt "infill_enable_travel_optimization description"
msgid "When enabled, the order in which the infill lines are printed is optimized to reduce the distance travelled. The reduction in travel time achieved very much depends on the model being sliced, infill pattern, density, etc. Note that, for some models that have many small areas of infill, the time to slice the model may be greatly increased."
-msgstr ""
+msgstr "Aktifleştirildiğinde, dolgu hatlarının baskı düzeni, hareketi azaltmak için optimize edilir. Elde edilen hareket zamanındaki azalma dilimlenen modele, dolgu şekline ve yoğunluğuna vs. bağlıdır. Birçok ufak dolgu bölgesine sahip bazı modeller için modelin dilimlenme süresi önemli ölçüde artabilir."
#: fdmprinter.def.json
msgctxt "material_flow_dependent_temperature label"
@@ -4943,9 +4937,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 ""
-"Yarı hızda sıkıştırılmış yukarı doğru hareket mesafesi.\n"
-"Bu katmanlarda malzemeyi çok fazla ısıtmayarak önceki katmanlarda daha iyi yapışma sağlayabilir. Sadece kablo yazdırmaya uygulanır."
+msgstr "Yarı hızda sıkıştırılmış yukarı doğru hareket mesafesi.\nBu katmanlarda malzemeyi çok fazla ısıtmayarak önceki katmanlarda daha iyi yapışma sağlayabilir. Sadece kablo yazdırmaya uygulanır."
#: fdmprinter.def.json
msgctxt "wireframe_top_jump label"
@@ -5055,42 +5047,42 @@ msgstr "Nozül ve aşağı yöndeki hatlar arasındaki mesafe. Daha büyük aç
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled label"
msgid "Use adaptive layers"
-msgstr ""
+msgstr "Uyarlanabilir Katmanların Kullanımı"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_enabled description"
msgid "Adaptive layers computes the layer heights depending on the shape of the model."
-msgstr ""
+msgstr "Uyarlanabilir katmanlar modelin şekline bağlı olarak katman yüksekliğini hesaplar."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation label"
msgid "Adaptive layers maximum variation"
-msgstr ""
+msgstr "Uyarlanabilir katmanların azami değişkenliği"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation description"
msgid "The maximum allowed height different from the base layer height in mm."
-msgstr ""
+msgstr "Mm bazında taban katmanı yüksekliğine göre izin verilen azami yükseklik."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step label"
msgid "Adaptive layers variation step size"
-msgstr ""
+msgstr "Uyarlanabilir katmanların değişkenlik adım boyu"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_variation_step description"
msgid "The difference in height of the next layer height compared to the previous one."
-msgstr ""
+msgstr "Bir önceki ve bir sonraki katman yüksekliği arasındaki yükseklik farkı."
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold label"
msgid "Adaptive layers threshold"
-msgstr ""
+msgstr "Uyarlanabilir katman eşiği"
#: fdmprinter.def.json
msgctxt "adaptive_layer_height_threshold description"
msgid "Threshold whether to use a smaller layer or not. This number is compared to the tan of the steepest slope in a layer."
-msgstr ""
+msgstr "Daha küçük bir katmanın kullanılıp kullanılmayacağını belirleyen eşik. Bu rakam bir katmandaki en dik eğimin tanjantına eşittir."
#: fdmprinter.def.json
msgctxt "command_line_settings label"
diff --git a/resources/i18n/zh_CN/cura.po b/resources/i18n/zh_CN/cura.po
index f2d1e5561f..e7632b0193 100644
--- a/resources/i18n/zh_CN/cura.po
+++ b/resources/i18n/zh_CN/cura.po
@@ -1,14 +1,14 @@
# Cura
-# Copyright (C) 2017 Ultimaker
+# Copyright (C) 2018 Ultimaker
# This file is distributed under the same license as the Cura package.
-# Ruben Dulek , 2017.
+# Ruben Dulek , 2018.
#
msgid ""
msgstr ""
-"Project-Id-Version: Cura 3.0\n"
+"Project-Id-Version: Cura 3.2\n"
"Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n"
-"POT-Creation-Date: 2017-08-02 16:53+0000\n"
-"PO-Revision-Date: 2017-11-30 13:05+0100\n"
+"POT-Creation-Date: 2018-01-29 09:48+0000\n"
+"PO-Revision-Date: 2018-02-05 13:25+0100\n"
"Last-Translator: Bothof \n"
"Language-Team: PCDotFan , Bothof \n"
"Language: zh_CN\n"
@@ -117,17 +117,17 @@ msgstr "配置文件已被合并并激活。"
#: /home/ruben/Projects/Cura/plugins/USBPrinting/USBPrinterOutputDevice.py:27
msgctxt "@item:inmenu"
msgid "USB printing"
-msgstr "USB 联机打印"
+msgstr "USB 打印"
#: /home/ruben/Projects/Cura/plugins/USBPrinting/USBPrinterOutputDevice.py:28
msgctxt "@action:button Preceded by 'Ready to'."
msgid "Print via USB"
-msgstr "通过 USB 联机打印"
+msgstr "通过 USB 打印"
#: /home/ruben/Projects/Cura/plugins/USBPrinting/USBPrinterOutputDevice.py:29
msgctxt "@info:tooltip"
msgid "Print via USB"
-msgstr "通过 USB 联机打印"
+msgstr "通过 USB 打印"
#: /home/ruben/Projects/Cura/plugins/USBPrinting/USBPrinterOutputDevice.py:31
msgctxt "@info:status"
@@ -189,7 +189,7 @@ msgstr "打印机固件"
#: /home/ruben/Projects/Cura/plugins/PrepareStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Prepare"
-msgstr ""
+msgstr "准备"
#: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:23
msgctxt "@action:button Preceded by 'Ready to'."
@@ -424,7 +424,7 @@ msgstr "线轴 {0} 上没有足够的材料。"
#, python-brace-format
msgctxt "@label"
msgid "Different PrintCore (Cura: {0}, Printer: {1}) selected for extruder {2}"
-msgstr "不同的打印头(Cura: {0},打印机: 为挤出机 {2} 选择了 {1})"
+msgstr "不同的打印头(Cura: {0},打印机: 为挤出机 {2} 选择了 {1})"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py:734
#, python-brace-format
@@ -562,7 +562,7 @@ msgstr "在您的浏览器中打开打印作业界面。"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml:239
msgctxt "@label Printer name"
msgid "Unknown"
-msgstr ""
+msgstr "未知"
#: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py:505
#, python-brace-format
@@ -597,7 +597,7 @@ msgstr "通过网络连接"
#: /home/ruben/Projects/Cura/plugins/MonitorStage/__init__.py:12
msgctxt "@item:inmenu"
msgid "Monitor"
-msgstr ""
+msgstr "监控"
#: /home/ruben/Projects/Cura/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py:66
#, python-brace-format
@@ -624,7 +624,7 @@ msgstr "无法获取更新信息。"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:579
msgctxt "@info:status"
msgid "SolidWorks reported errors, while opening your file. We recommend to solve these issues inside SolidWorks itself."
-msgstr ""
+msgstr "打开文件时,SolidWorks 报错。我们建议在 SolidWorks 内部解决这些问题。"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:591
msgctxt "@info:status"
@@ -632,7 +632,7 @@ msgid ""
"Found no models inside your drawing. Could you please check it's content again and make sure one part or assembly is inside?\n"
"\n"
" Thanks!."
-msgstr ""
+msgstr "在您的图纸中找不到模型。请再次检查图纸内容,确保里面有一个零件或组件。\n\n谢谢!"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksReader.py:595
msgctxt "@info:status"
@@ -640,7 +640,7 @@ msgid ""
"Found more then one part or assembly inside your drawing. We currently only support drawings with exactly one part or assembly inside.\n"
"\n"
"Sorry!"
-msgstr ""
+msgstr "在您的图纸中找到一个以上的零件或组件。我们目前只支持里面正好有一个零件或组件的图纸。\n\n很抱歉!"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:25
msgctxt "@item:inlistbox"
@@ -655,7 +655,7 @@ msgstr "SolidWorks 组件文件"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:33
msgctxt "@item:inlistbox"
msgid "SolidWorks drawing file"
-msgstr ""
+msgstr "SolidWorks 图纸文件"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:48
msgctxt "@info:status"
@@ -665,7 +665,7 @@ msgid ""
"\n"
"With kind regards\n"
" - Thomas Karl Pietrowski"
-msgstr ""
+msgstr "尊敬的客户:\n我们无法在您的系统中找到有效的 SolidWorks 软件。这意味着您的系统中没有安装 SolidWorks,或者您没有获得有效的许可。请确保 SolidWorks 的运行没有任何问题并/或联系您的 ICT。\n\n此致\n - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/__init__.py:57
msgctxt "@info:status"
@@ -675,7 +675,7 @@ msgid ""
"\n"
"With kind regards\n"
" - Thomas Karl Pietrowski"
-msgstr ""
+msgstr "尊敬的客户:\n您当前正在非 Windows 操作系统上运行此插件。此插件只能在装有 SolidWorks 且拥有有效许可的 Windows 系统上运行。请在装有 SolidWorks 的 Windows 计算机上安装此插件。\n\n此致\n - Thomas Karl Pietrowski"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:70
msgid "Configure"
@@ -683,7 +683,7 @@ msgstr "配置"
#: /home/ruben/Projects/Cura/plugins/CuraSolidWorksPlugin/SolidWorksDialogHandler.py:71
msgid "Installation guide for SolidWorks macro"
-msgstr ""
+msgstr "SolidWorks 宏的安装指南"
#: /home/ruben/Projects/Cura/plugins/SimulationView/__init__.py:14
msgctxt "@item:inlistbox"
@@ -707,7 +707,7 @@ msgstr "修改 G-Code 文件"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:43
msgctxt "@info"
msgid "Cura collects anonymized usage statistics."
-msgstr ""
+msgstr "Cura 将收集匿名的使用统计数据。"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:46
msgctxt "@info:title"
@@ -717,22 +717,22 @@ msgstr "正在收集数据"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:48
msgctxt "@action:button"
msgid "Allow"
-msgstr ""
+msgstr "允许"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:49
msgctxt "@action:tooltip"
msgid "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing."
-msgstr ""
+msgstr "允许 Cura 发送匿名的使用统计数据,以帮助确定将来 Cura 的改进优先顺序。已发送您的一些偏好和设置,Cura 版本和您正在切片的模型的散列值。"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:50
msgctxt "@action:button"
msgid "Disable"
-msgstr ""
+msgstr "禁用"
#: /home/ruben/Projects/Cura/plugins/SliceInfoPlugin/SliceInfo.py:51
msgctxt "@action:tooltip"
msgid "Don't allow Cura to send anonymized usage statistics. You can enable it again in the preferences."
-msgstr ""
+msgstr "不允许 Cura 发送匿名的使用统计数据。您可以在偏好中再次启用。"
#: /home/ruben/Projects/Cura/plugins/LegacyProfileReader/__init__.py:14
msgctxt "@item:inlistbox"
@@ -742,14 +742,14 @@ msgstr "Cura 15.04 配置文件"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/__init__.py:15
msgctxt "@item:inlistbox"
msgid "Blender file"
-msgstr ""
+msgstr "Blender 文件"
#: /home/ruben/Projects/Cura/plugins/CuraBlenderPlugin/CadIntegrationUtils/CommonReader.py:199
msgctxt "@info:status"
msgid ""
"Could not export using \"{}\" quality!\n"
"Felt back to \"{}\"."
-msgstr ""
+msgstr "无法使用 \"{}\" 导出质量!\n返回 \"{}\"。"
#: /home/ruben/Projects/Cura/plugins/GCodeProfileReader/__init__.py:14
#: /home/ruben/Projects/Cura/plugins/GCodeReader/__init__.py:14
@@ -936,12 +936,12 @@ msgstr "Cura 配置文件"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:12
msgctxt "@item:inmenu"
msgid "Profile Assistant"
-msgstr ""
+msgstr "配置文件助手"
#: /home/ruben/Projects/Cura/plugins/CuraPrintProfileCreator/__init__.py:17
msgctxt "@item:inlistbox"
msgid "Profile Assistant"
-msgstr ""
+msgstr "配置文件助手"
#: /home/ruben/Projects/Cura/plugins/3MFWriter/__init__.py:30
msgctxt "@item:inlistbox"
@@ -1017,7 +1017,7 @@ msgstr "Skirt"
#: /home/ruben/Projects/Cura/cura/PrintInformation.py:106
msgctxt "@tooltip"
msgid "Travel"
-msgstr "移动"
+msgstr "空驶"
#: /home/ruben/Projects/Cura/cura/PrintInformation.py:107
msgctxt "@tooltip"
@@ -1061,7 +1061,7 @@ msgstr "文件已存在"
#, python-brace-format
msgctxt "@label Don't translate the XML tag !"
msgid "The file {0} already exists. Are you sure you want to overwrite it?"
-msgstr "文件 {0} 已存在。 您确定要替换它吗?"
+msgstr "文件 {0} 已存在。 您确定要覆盖它吗?"
#: /home/ruben/Projects/Cura/cura/Settings/ContainerManager.py:872
msgctxt "@label"
@@ -1139,13 +1139,13 @@ msgstr "无法从 {0} 导入配置文件: {1}<
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "This profile {0} contains incorrect data, could not import it."
-msgstr ""
+msgstr "此配置文件 {0} 包含错误数据,无法导入。"
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:240
#, python-brace-format
msgctxt "@info:status Don't translate the XML tags or !"
msgid "The machine defined in profile {0} doesn't match with your current machine, could not import it."
-msgstr ""
+msgstr "配置文件 {0} 中定义的机器与您当前的机器不匹配,无法导入。"
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:274
#, python-brace-format
@@ -1157,7 +1157,7 @@ msgstr "已成功导入配置文件 {0}"
#, python-brace-format
msgctxt "@info:status"
msgid "File {0} does not contain any valid profile."
-msgstr ""
+msgstr "文件 {0} 不包含任何有效的配置文件。"
#: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:280
#, python-brace-format
@@ -1185,7 +1185,7 @@ msgstr "无法为当前配置找到质量类型 {0}。"
#, python-brace-format
msgctxt "@label"
msgid "Group #{group_nr}"
-msgstr ""
+msgstr "组 #{group_nr}"
#: /home/ruben/Projects/Cura/cura/BuildVolume.py:100
msgctxt "@info:status"
@@ -1244,7 +1244,7 @@ msgid ""
"
A fatal error has occurred. Please send us this Crash Report to fix the problem
\n"
"
Please use the \"Send report\" button to post a bug report automatically to our servers