Merge branch 'master' into feature_preheat_extruder

# Conflicts:
#	plugins/USBPrinting/USBPrinterOutputController.py
#	plugins/USBPrinting/USBPrinterOutputDevice.py
This commit is contained in:
fieldOfView 2018-01-27 16:35:55 +01:00
commit ed9943c732
114 changed files with 3794 additions and 3108 deletions

4
Jenkinsfile vendored
View File

@ -1,5 +1,5 @@
timeout(time: 2, unit: "HOURS") {
parallel_nodes(['linux && cura', 'windows && cura']) {
parallel_nodes(['linux && cura', 'windows && cura']) {
timeout(time: 2, unit: "HOURS") {
// Prepare building
stage('Prepare') {
// Ensure we start with a clean build directory.

View File

@ -41,25 +41,7 @@ Please check out [Wiki page](https://github.com/Ultimaker/Cura/wiki/Cura-Setting
Translating Cura
----------------
If you'd like to contribute a translation of Cura, please first look for [any existing translation](https://github.com/Ultimaker/Cura/tree/master/resources/i18n). If your language is already there in the source code but not in Cura's interface, it may be partially translated.
There are four files that need to be translated for Cura:
1. https://github.com/Ultimaker/Cura/blob/master/resources/i18n/cura.pot
2. https://github.com/Ultimaker/Cura/blob/master/resources/i18n/fdmextruder.def.json.pot
3. https://github.com/Ultimaker/Cura/blob/master/resources/i18n/fdmprinter.def.json.pot (This one is the most work.)
4. https://github.com/Ultimaker/Uranium/blob/master/resources/i18n/uranium.pot
Copy these files and rename them to `*.po` (remove the `t`). Then create the actual translations by filling in the empty `msgstr` entries. These are gettext files, which are plain text so you can open them with any text editor such as Notepad or GEdit, but it is probably easier with a specialised tool such as [POEdit](https://poedit.net/) or [Virtaal](http://virtaal.translatehouse.org/).
Do not hestiate to ask us about a translation or the meaning of some text via Github Issues.
Once the translation is complete, it's probably best to test them in Cura. Use your favourite software to convert the .po file to a .mo file (such as [GetText](https://www.gnu.org/software/gettext/)). Then put the .mo files in the `.../resources/i18n/<language code>/LC_MESSAGES` folder in your Cura installation. Then find your Cura configuration file (next to the log as described above, except on Linux where it is located in `~/.config/cura`) and change the language preference to the name of the folder you just created. Then start Cura. If working correctly, your Cura should now be translated.
To submit your translation, ideally you would make two pull requests where all `*.po` files are located in that same `<language code>` folder in the resources of both the Cura and Uranium repositories. Put `cura.po`, `fdmprinter.def.json.po` and `fdmextruder.def.json.po` in the Cura repository, and put `uranium.po` in the Uranium repository. Then submit the pull requests to Github. For people with less experience with Git, you can also e-mail the translations to the e-mail address listed at the top of the [cura.pot](https://github.com/Ultimaker/Cura/blob/master/resources/i18n/cura.pot) file as the `Report-Msgid-Bugs-To` entry and we'll make sure it gets checked and included.
After the translation is submitted, the Cura maintainers will check for its completeness and check whether it is consistent. We will take special care to look for common mistakes, such as translating mark-up `<message>` code and such. We are often not fluent in every language, so we expect the translator and the international users to make corrections where necessary. Of course, there will always be some mistakes in every translation.
When the next Cura release comes around, some of the texts will have changed and some new texts will have been added. Around the time when the beta is released we will invoke a string freeze, meaning that no developer is allowed to make changes to the texts. Then we will update the translation template `.pot` files and ask all our translators to update their translations. If you are unable to update the translation in time for the actual release, we will remove the language from the drop-down menu in the Preferences window. The translation stays in Cura however, so that someone might pick it up again later and update it with the newest texts. Also, users who had previously selected the language can still continue Cura in their language but English text will appear among the original text.
Please check out [Wiki page](https://github.com/Ultimaker/Cura/wiki/Translating-Cura) about how to translate Cura into other languages.
License
----------------

View File

@ -88,3 +88,5 @@ class ArrangeObjectsJob(Job):
no_full_solution_message = Message(i18n_catalog.i18nc("@info:status", "Unable to find a location within the build volume for all objects"),
title = i18n_catalog.i18nc("@info:title", "Can't Find Location"))
no_full_solution_message.show()
self.finished.emit(self)

View File

@ -1,6 +1,7 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from cura.Scene.CuraSceneNode import CuraSceneNode
from cura.Settings.ExtruderManager import ExtruderManager
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.i18n import i18nCatalog
@ -25,7 +26,7 @@ catalog = i18nCatalog("cura")
import numpy
import math
from typing import List
from typing import List, Optional
# Setting for clearance around the prime
PRIME_CLEARANCE = 6.5
@ -241,6 +242,44 @@ class BuildVolume(SceneNode):
for child_node in group_node.getAllChildren():
child_node._outside_buildarea = group_node._outside_buildarea
## Update the outsideBuildArea of a single node, given bounds or current build volume
def checkBoundsAndUpdate(self, node: CuraSceneNode, bounds: Optional[AxisAlignedBox] = None):
if not isinstance(node, CuraSceneNode):
return
if bounds is None:
build_volume_bounding_box = self.getBoundingBox()
if build_volume_bounding_box:
# It's over 9000!
build_volume_bounding_box = build_volume_bounding_box.set(bottom=-9001)
else:
# No bounding box. This is triggered when running Cura from command line with a model for the first time
# In that situation there is a model, but no machine (and therefore no build volume.
return
else:
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:
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.
def rebuild(self):
if not self._width or not self._height or not self._depth:

View File

@ -94,6 +94,10 @@ class CuraActions(QObject):
removed_group_nodes.append(group_node)
op.addOperation(SetParentOperation(remaining_nodes_in_group[0], group_node.getParent()))
op.addOperation(RemoveSceneNodeOperation(group_node))
# Reset the print information
Application.getInstance().getController().getScene().sceneChanged.emit(node)
op.push()
## Set the extruder that should be used to print the selection.

View File

@ -142,6 +142,7 @@ class CuraApplication(QtApplication):
if not hasattr(sys, "frozen"):
Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources"))
self._use_gui = True
self._open_file_queue = [] # Files to open when plug-ins are loaded.
# Need to do this before ContainerRegistry tries to load the machines
@ -278,6 +279,11 @@ class CuraApplication(QtApplication):
# We need them to simplify the switching between materials.
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
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)
empty_variant_container = copy.deepcopy(empty_container)
empty_variant_container.setMetaDataEntry("id", "empty_variant")
empty_variant_container.addMetaDataEntry("type", "variant")
@ -452,7 +458,7 @@ class CuraApplication(QtApplication):
elif choice == "always_keep":
# don't show dialog and KEEP the profile
self.discardOrKeepProfileChangesClosed("keep")
else:
elif self._use_gui:
# ALWAYS ask whether to keep or discard the profile
self.showDiscardOrKeepProfileChanges.emit()
has_user_interaction = True
@ -652,12 +658,47 @@ class CuraApplication(QtApplication):
def run(self):
self.preRun()
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Setting up scene..."))
# Check if we should run as single instance or not
self._setUpSingleInstanceServer()
# Setup scene and build volume
root = self.getController().getScene().getRoot()
self._volume = BuildVolume.BuildVolume(self.getController().getScene().getRoot())
Arrange.build_volume = self._volume
# initialize info objects
self._print_information = PrintInformation.PrintInformation()
self._cura_actions = CuraActions.CuraActions(self)
# Detect in which mode to run and execute that mode
if self.getCommandLineOption("headless", False):
self.runWithoutGUI()
else:
self.runWithGUI()
# Pre-load files if requested
for file_name in self.getCommandLineOption("file", []):
self._openFile(file_name)
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.exec_()
## Run Cura without GUI elements and interaction (server mode).
def runWithoutGUI(self):
self._use_gui = False
self.closeSplash()
## Run Cura with GUI (desktop mode).
def runWithGUI(self):
self._use_gui = True
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Setting up scene..."))
controller = self.getController()
# Initialize UI state
controller.setActiveStage("PrepareStage")
controller.setActiveView("SolidView")
controller.setCameraTool("CameraTool")
@ -669,67 +710,44 @@ class CuraApplication(QtApplication):
Selection.selectionChanged.connect(self.onSelectionChanged)
root = controller.getScene().getRoot()
# The platform is a child of BuildVolume
self._volume = BuildVolume.BuildVolume(root)
# Set the build volume of the arranger to the used build volume
Arrange.build_volume = self._volume
# Set default background color for scene
self.getRenderer().setBackgroundColor(QColor(245, 245, 245))
# Initialize platform physics
self._physics = PlatformPhysics.PlatformPhysics(controller, self._volume)
# Initialize camera
root = controller.getScene().getRoot()
camera = Camera("3d", root)
camera.setPosition(Vector(-80, 250, 700))
camera.setPerspective(True)
camera.lookAt(Vector(0, 0, 0))
controller.getScene().setActiveCamera("3d")
camera_tool = self.getController().getTool("CameraTool")
# Initialize camera tool
camera_tool = controller.getTool("CameraTool")
camera_tool.setOrigin(Vector(0, 100, 0))
camera_tool.setZoomRange(0.1, 200000)
# Initialize camera animations
self._camera_animation = CameraAnimation.CameraAnimation()
self._camera_animation.setCameraTool(self.getController().getTool("CameraTool"))
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading interface..."))
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(ObjectsModel, "Cura", 1, 2, "ObjectsModel", self.getObjectsModel)
qmlRegisterSingletonType(BuildPlateModel, "Cura", 1, 2, "BuildPlateModel", self.getBuildPlateModel)
qmlRegisterSingletonType(CuraSceneController, "Cura", 1, 2, "SceneController", self.getCuraSceneController)
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
# Initialize QML engine
self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml"))
self._qml_import_paths.append(Resources.getPath(self.ResourceTypes.QmlFiles))
self.initializeEngine()
run_without_gui = self.getCommandLineOption("headless", False)
if not run_without_gui:
self.initializeEngine()
controller.setActiveStage("PrepareStage")
# Make sure the correct stage is activated after QML is loaded
controller.setActiveStage("PrepareStage")
if run_without_gui or self._engine.rootObjects:
self.closeSplash()
# Hide the splash screen
self.closeSplash()
for file_name in self.getCommandLineOption("file", []):
self._openFile(file_name)
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.exec_()
def hasGui(self):
return self._use_gui
def getMachineManager(self, *args) -> MachineManager:
if self._machine_manager is None:
@ -797,15 +815,25 @@ class CuraApplication(QtApplication):
# \param engine The QML engine.
def registerObjects(self, engine):
super().registerObjects(engine)
# global contexts
engine.rootContext().setContextProperty("Printer", self)
engine.rootContext().setContextProperty("CuraApplication", self)
self._print_information = PrintInformation.PrintInformation()
engine.rootContext().setContextProperty("PrintInformation", self._print_information)
self._cura_actions = CuraActions.CuraActions(self)
engine.rootContext().setContextProperty("CuraActions", self._cura_actions)
qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type")
qmlRegisterSingletonType(CuraSceneController, "Cura", 1, 2, "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(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
qmlRegisterSingletonType(ObjectsModel, "Cura", 1, 2, "ObjectsModel", self.getObjectsModel)
qmlRegisterSingletonType(BuildPlateModel, "Cura", 1, 2, "BuildPlateModel", self.getBuildPlateModel)
qmlRegisterType(InstanceContainer, "Cura", 1, 0, "InstanceContainer")
qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel")
qmlRegisterType(ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel")
@ -1035,8 +1063,9 @@ class CuraApplication(QtApplication):
Selection.add(node)
## Delete all nodes containing mesh data in the scene.
# \param only_selectable. Set this to False to delete objects from all build plates
@pyqtSlot()
def deleteAll(self):
def deleteAll(self, only_selectable = True):
Logger.log("i", "Clearing scene")
if not self.getController().getToolsEnabled():
return
@ -1047,7 +1076,9 @@ class CuraApplication(QtApplication):
continue
if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
if not node.isSelectable():
if only_selectable and not node.isSelectable():
continue
if not node.callDecoration("isSliceable") and not node.callDecoration("getLayerData") and not node.callDecoration("isGroup"):
continue # Only remove nodes that are selectable.
if node.getParent() and node.getParent().callDecoration("isGroup"):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
@ -1058,16 +1089,12 @@ class CuraApplication(QtApplication):
for node in nodes:
op.addOperation(RemoveSceneNodeOperation(node))
# Reset the print information
self.getController().getScene().sceneChanged.emit(node)
op.push()
Selection.clear()
# Reset the print information:
self.getController().getScene().sceneChanged.emit(node)
# self._print_information.setToZeroPrintInformation(self.getBuildPlateModel().activeBuildPlate)
# stay on the same build plate
#self.getCuraSceneController().setActiveBuildPlate(0) # Select first build plate
## Reset all translation on nodes with mesh data.
@pyqtSlot()
def resetAllTranslation(self):
@ -1342,6 +1369,7 @@ class CuraApplication(QtApplication):
pass
fileLoaded = pyqtSignal(str)
fileCompleted = pyqtSignal(str)
def _reloadMeshFinished(self, job):
# TODO; This needs to be fixed properly. We now make the assumption that we only load a single mesh!
@ -1426,7 +1454,7 @@ class CuraApplication(QtApplication):
self._currently_loading_files.append(f)
if extension in self._non_sliceable_extensions:
self.deleteAll()
self.deleteAll(only_selectable = False)
job = ReadMeshJob(f)
job.finished.connect(self._readMeshFinished)
@ -1459,6 +1487,7 @@ class CuraApplication(QtApplication):
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:
@ -1495,8 +1524,8 @@ class CuraApplication(QtApplication):
# Step is for skipping tests to make it a lot faster. it also makes the outcome somewhat rougher
node, _ = arranger.findNodePlacement(node, offset_shape_arr, hull_shape_arr, step = 10)
# This node is deepcopied from some other node which already has a BuildPlateDecorator, but the deepcopy
# of BuildPlateDecorator produces one that's assoicated with build plate -1. So, here we need to check if
# This node is deep copied from some other node which already has a BuildPlateDecorator, but the deepcopy
# of BuildPlateDecorator produces one that's associated with build plate -1. So, here we need to check if
# the BuildPlateDecorator exists or not and always set the correct build plate number.
build_plate_decorator = node.getDecorator(BuildPlateDecorator)
if build_plate_decorator is None:
@ -1508,6 +1537,8 @@ class CuraApplication(QtApplication):
op.push()
scene.sceneChanged.emit(node)
self.fileCompleted.emit(filename)
def addNonSliceableExtension(self, extension):
self._non_sliceable_extensions.append(extension)

View File

@ -18,12 +18,13 @@ class OneAtATimeIterator(Iterator.Iterator):
def _fillStack(self):
node_list = []
for node in self._scene_node.getChildren():
if not isinstance(node, SceneNode):
if not issubclass(type(node), SceneNode):
continue
if node.callDecoration("getConvexHull"):
node_list.append(node)
if len(node_list) < 2:
self._node_stack = node_list[:]
return

View File

@ -8,7 +8,9 @@ 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
@ -65,7 +67,7 @@ class PrintInformation(QObject):
self._backend = Application.getInstance().getBackend()
if self._backend:
self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
Application.getInstance().getController().getScene().sceneChanged.connect(self.setToZeroPrintInformation)
Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged)
self._base_name = ""
self._abbr_machine = ""
@ -395,12 +397,25 @@ class PrintInformation(QObject):
return result
# Simulate message with zero time duration
def setToZeroPrintInformation(self, build_plate_number):
temp_message = {}
if build_plate_number not in self._print_time_message_values:
self._print_time_message_values[build_plate_number] = {}
for key in self._print_time_message_values[build_plate_number].keys():
temp_message[key] = 0
def setToZeroPrintInformation(self, build_plate):
# Construct the 0-time message
temp_message = {}
if build_plate not in self._print_time_message_values:
self._print_time_message_values[build_plate] = {}
for key in self._print_time_message_values[build_plate].keys():
temp_message[key] = 0
temp_material_amounts = [0]
self._onPrintDurationMessage(build_plate_number, temp_message, temp_material_amounts)
self._onPrintDurationMessage(build_plate, temp_message, temp_material_amounts)
## 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")\
or not scene_node.callDecoration("getBuildPlateNumber") == self._active_build_plate:
return
self.setToZeroPrintInformation(self._active_build_plate)

View File

@ -100,7 +100,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
if batched_lines_count >= max_chars_per_line:
file_data_bytes_list.append(self._compressDataAndNotifyQt("".join(batched_lines)))
batched_lines = []
batched_lines_count
batched_lines_count = 0
# Don't miss the last batch (If any)
if len(batched_lines) != 0:

View File

@ -24,7 +24,10 @@ class ConvexHullNode(SceneNode):
self._original_parent = parent
# Color of the drawn convex hull
self._color = Color(*Application.getInstance().getTheme().getColor("convex_hull").getRgb())
if Application.getInstance().hasGui():
self._color = Color(*Application.getInstance().getTheme().getColor("convex_hull").getRgb())
else:
self._color = Color(0, 0, 0)
# The y-coordinate of the convex hull mesh. Must not be 0, to prevent z-fighting.
self._mesh_height = 0.1

View File

@ -9,7 +9,7 @@ from copy import deepcopy
class CuraSceneNode(SceneNode):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._outside_buildarea = True
self._outside_buildarea = False
def setOutsideBuildArea(self, new_value):
self._outside_buildarea = new_value

View File

@ -211,6 +211,34 @@ class CuraContainerRegistry(ContainerRegistry):
return { "status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, str(e))}
if profile_or_list:
# Ensure it is always a list of profiles
if not isinstance(profile_or_list, list):
profile_or_list = [profile_or_list]
# First check if this profile is suitable for this machine
global_profile = None
if len(profile_or_list) == 1:
global_profile = profile_or_list[0]
else:
for profile in profile_or_list:
if not profile.getMetaDataEntry("extruder"):
global_profile = profile
break
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 <filename> or <message>!", "This profile <filename>{0}</filename> 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)
return { "status": "error",
"message": catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "The machine defined in profile <filename>{0}</filename> doesn't match with your current machine, could not import it.", file_name)}
name_seed = os.path.splitext(os.path.basename(file_name))[0]
new_name = self.uniqueName(name_seed)
@ -245,6 +273,9 @@ class CuraContainerRegistry(ContainerRegistry):
return {"status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile_or_list[0].getName())}
# This message is throw when the profile reader doesn't find any profile in the file
return {"status": "error", "message": catalog.i18nc("@info:status", "File {0} does not contain any valid profile.", file_name)}
# If it hasn't returned by now, none of the plugins loaded the profile successfully.
return {"status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type or is corrupted.", file_name)}
@ -418,7 +449,14 @@ class CuraContainerRegistry(ContainerRegistry):
if not extruder_stacks:
self.addExtruderStackForSingleExtrusionMachine(container, "fdmextruder")
def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id):
#
# new_global_quality_changes is optional. It is only used in project loading for a scenario like this:
# - override the current machine
# - create new for custom quality profile
# new_global_quality_changes is the new global quality changes container in this scenario.
# create_new_ids indicates if new unique ids must be created
#
def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id, new_global_quality_changes = None, create_new_ids = True):
new_extruder_id = extruder_id
extruder_definitions = self.findDefinitionContainers(id = new_extruder_id)
@ -427,7 +465,7 @@ class CuraContainerRegistry(ContainerRegistry):
return
extruder_definition = extruder_definitions[0]
unique_name = self.uniqueName(machine.getName() + " " + new_extruder_id)
unique_name = self.uniqueName(machine.getName() + " " + new_extruder_id) if create_new_ids else machine.getName() + " " + new_extruder_id
extruder_stack = ExtruderStack.ExtruderStack(unique_name)
extruder_stack.setName(extruder_definition.getName())
@ -437,7 +475,7 @@ class CuraContainerRegistry(ContainerRegistry):
from cura.CuraApplication import CuraApplication
# create a new definition_changes container for the extruder stack
definition_changes_id = self.uniqueName(extruder_stack.getId() + "_settings")
definition_changes_id = self.uniqueName(extruder_stack.getId() + "_settings") if create_new_ids else extruder_stack.getId() + "_settings"
definition_changes_name = definition_changes_id
definition_changes = InstanceContainer(definition_changes_id)
definition_changes.setName(definition_changes_name)
@ -464,7 +502,7 @@ class CuraContainerRegistry(ContainerRegistry):
extruder_stack.setDefinitionChanges(definition_changes)
# create empty user changes container otherwise
user_container_id = self.uniqueName(extruder_stack.getId() + "_user")
user_container_id = self.uniqueName(extruder_stack.getId() + "_user") if create_new_ids else extruder_stack.getId() + "_user"
user_container_name = user_container_id
user_container = InstanceContainer(user_container_id)
user_container.setName(user_container_name)
@ -514,8 +552,12 @@ class CuraContainerRegistry(ContainerRegistry):
quality_id = "empty_quality"
extruder_stack.setQualityById(quality_id)
if machine.qualityChanges.getId() not in ("empty", "empty_quality_changes"):
extruder_quality_changes_container = self.findInstanceContainers(name = machine.qualityChanges.getName(), extruder = extruder_id)
machine_quality_changes = machine.qualityChanges
if new_global_quality_changes is not None:
machine_quality_changes = new_global_quality_changes
if machine_quality_changes.getId() not in ("empty", "empty_quality_changes"):
extruder_quality_changes_container = self.findInstanceContainers(name = machine_quality_changes.getName(), extruder = extruder_id)
if extruder_quality_changes_container:
extruder_quality_changes_container = extruder_quality_changes_container[0]
@ -525,31 +567,34 @@ class CuraContainerRegistry(ContainerRegistry):
# 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
# the folder to see if the quality_changes exists.
extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine.qualityChanges.getName())
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)
else:
# if we still cannot find a quality changes container for the extruder, create a new one
container_id = self.uniqueName(extruder_stack.getId() + "_user")
container_name = machine.qualityChanges.getName()
container_name = machine_quality_changes.getName()
container_id = self.uniqueName(extruder_stack.getId() + "_qc_" + container_name)
extruder_quality_changes_container = InstanceContainer(container_id)
extruder_quality_changes_container.setName(container_name)
extruder_quality_changes_container.addMetaDataEntry("type", "quality_changes")
extruder_quality_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
extruder_quality_changes_container.addMetaDataEntry("extruder", extruder_stack.definition.getId())
extruder_quality_changes_container.addMetaDataEntry("quality_type", machine.qualityChanges.getMetaDataEntry("quality_type"))
extruder_quality_changes_container.setDefinition(machine.qualityChanges.getDefinition().getId())
extruder_quality_changes_container.addMetaDataEntry("quality_type", machine_quality_changes.getMetaDataEntry("quality_type"))
extruder_quality_changes_container.setDefinition(machine_quality_changes.getDefinition().getId())
self.addContainer(extruder_quality_changes_container)
extruder_stack.qualityChanges = extruder_quality_changes_container
if not extruder_quality_changes_container:
Logger.log("w", "Could not find quality_changes named [%s] for extruder [%s]",
machine.qualityChanges.getName(), extruder_stack.getId())
machine_quality_changes.getName(), extruder_stack.getId())
else:
# move all per-extruder settings to the extruder's quality changes
for qc_setting_key in machine.qualityChanges.getAllKeys():
for qc_setting_key in machine_quality_changes.getAllKeys():
settable_per_extruder = machine.getProperty(qc_setting_key, "settable_per_extruder")
if settable_per_extruder:
setting_value = machine.qualityChanges.getProperty(qc_setting_key, "value")
setting_value = machine_quality_changes.getProperty(qc_setting_key, "value")
setting_definition = machine.getSettingDefinition(qc_setting_key)
new_instance = SettingInstance(setting_definition, definition_changes)
@ -558,7 +603,7 @@ class CuraContainerRegistry(ContainerRegistry):
extruder_quality_changes_container.addInstance(new_instance)
extruder_quality_changes_container.setDirty(True)
machine.qualityChanges.removeInstance(qc_setting_key, postpone_emit=True)
machine_quality_changes.removeInstance(qc_setting_key, postpone_emit=True)
else:
extruder_stack.setQualityChangesById("empty_quality_changes")
@ -566,8 +611,8 @@ class CuraContainerRegistry(ContainerRegistry):
# Also need to fix the other qualities that are suitable for this machine. Those quality changes may still have
# per-extruder settings in the container for the machine instead of the extruder.
if machine.qualityChanges.getId() not in ("empty", "empty_quality_changes"):
quality_changes_machine_definition_id = machine.qualityChanges.getDefinition().getId()
if machine_quality_changes.getId() not in ("empty", "empty_quality_changes"):
quality_changes_machine_definition_id = machine_quality_changes.getDefinition().getId()
else:
whole_machine_definition = machine.definition
machine_entry = machine.definition.getMetaDataEntry("machine")
@ -587,7 +632,7 @@ class CuraContainerRegistry(ContainerRegistry):
qc_groups[qc_name] = []
qc_groups[qc_name].append(qc)
# try to find from the quality changes cura directory too
quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine.qualityChanges.getName())
quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine_quality_changes.getName())
if quality_changes_container:
qc_groups[qc_name].append(quality_changes_container)

View File

@ -63,6 +63,7 @@ class CuraStackBuilder:
next_stack = 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):
@ -81,6 +82,11 @@ class CuraStackBuilder:
next_stack = new_global_stack
)
new_global_stack.addExtruder(new_extruder)
registry.addContainer(new_extruder)
# 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).
registry.addContainer(new_global_stack)
return new_global_stack
@ -135,9 +141,7 @@ class CuraStackBuilder:
# 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
# setting properties will not result in incomplete containers being added.
registry = ContainerRegistry.getInstance()
registry.addContainer(stack)
registry.addContainer(user_container)
ContainerRegistry.getInstance().addContainer(user_container)
return stack
@ -181,9 +185,7 @@ class CuraStackBuilder:
if "quality_changes" in kwargs:
stack.setQualityChangesById(kwargs["quality_changes"])
registry = ContainerRegistry.getInstance()
registry.addContainer(stack)
registry.addContainer(user_container)
ContainerRegistry.getInstance().addContainer(user_container)
return stack

View File

@ -407,6 +407,12 @@ class ExtruderManager(QObject):
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)
if extruders_changed:
self.extrudersChanged.emit(global_stack_id)
self.extrudersAdded.emit()
@ -496,6 +502,89 @@ class ExtruderManager(QObject):
def getInstanceExtruderValues(self, key):
return ExtruderManager.getExtruderValues(key)
## Updates the material container to a material that matches the material diameter set for the printer
def updateMaterialForDiameter(self, extruder_position: int):
global_stack = Application.getInstance().getGlobalContainerStack()
if not global_stack:
return
if not global_stack.getMetaDataEntry("has_materials", False):
return
extruder_stack = global_stack.extruders[str(extruder_position)]
material_diameter = extruder_stack.material.getProperty("material_diameter", "value")
if not material_diameter:
# in case of "empty" material
material_diameter = 0
material_approximate_diameter = str(round(material_diameter))
machine_diameter = extruder_stack.definitionChanges.getProperty("material_diameter", "value")
if not machine_diameter:
if extruder_stack.definition.hasProperty("material_diameter", "value"):
machine_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))
if material_approximate_diameter != machine_approximate_diameter:
Logger.log("i", "The the currently active material(s) do not match the diameter set for the printer. Finding alternatives.")
if global_stack.getMetaDataEntry("has_machine_materials", False):
materials_definition = global_stack.definition.getId()
has_material_variants = global_stack.getMetaDataEntry("has_variants", False)
else:
materials_definition = "fdmprinter"
has_material_variants = False
old_material = extruder_stack.material
search_criteria = {
"type": "material",
"approximate_diameter": machine_approximate_diameter,
"material": old_material.getMetaDataEntry("material", "value"),
"brand": old_material.getMetaDataEntry("brand", "value"),
"supplier": old_material.getMetaDataEntry("supplier", "value"),
"color_name": old_material.getMetaDataEntry("color_name", "value"),
"definition": materials_definition
}
if has_material_variants:
search_criteria["variant"] = extruder_stack.variant.getId()
container_registry = Application.getInstance().getContainerRegistry()
empty_material = container_registry.findInstanceContainers(id = "empty_material")[0]
if old_material == empty_material:
search_criteria.pop("material", None)
search_criteria.pop("supplier", None)
search_criteria.pop("brand", None)
search_criteria.pop("definition", None)
search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material")
materials = container_registry.findInstanceContainers(**search_criteria)
if not materials:
# Same material with new diameter is not found, search for generic version of the same material type
search_criteria.pop("supplier", None)
search_criteria.pop("brand", None)
search_criteria["color_name"] = "Generic"
materials = container_registry.findInstanceContainers(**search_criteria)
if not materials:
# Generic material with new diameter is not found, search for preferred material
search_criteria.pop("color_name", None)
search_criteria.pop("material", None)
search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material")
materials = container_registry.findInstanceContainers(**search_criteria)
if not materials:
# Preferred material with new diameter is not found, search for any material
search_criteria.pop("id", None)
materials = container_registry.findInstanceContainers(**search_criteria)
if not materials:
# Just use empty material as a final fallback
materials = [empty_material]
Logger.log("i", "Selecting new material: %s", materials[0].getId())
extruder_stack.material = materials[0]
## Get the value for a setting from a specific extruder.
#
# This is exposed to SettingFunction to use in value functions.

View File

@ -3,6 +3,7 @@
from typing import Any, TYPE_CHECKING, Optional
from UM.Application import Application
from UM.Decorators import override
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
from UM.Settings.ContainerStack import ContainerStack
@ -18,10 +19,6 @@ if TYPE_CHECKING:
from cura.Settings.GlobalStack import GlobalStack
_EXTRUDER_SPECIFIC_DEFINITION_CHANGES_SETTINGS = ["machine_nozzle_size",
"material_diameter"]
## Represents an Extruder and its related containers.
#
#
@ -53,20 +50,48 @@ class ExtruderStack(CuraContainerStack):
# 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.
if self.getMetaDataEntry("position") == "0":
for key in _EXTRUDER_SPECIFIC_DEFINITION_CHANGES_SETTINGS:
setting_value = stack.definitionChanges.getProperty(key, "value")
if setting_value is None:
continue
#
# 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.
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)
keys_to_copy = ["material_diameter", "machine_nozzle_size"] # these will be copied over to all extruders
stack.definitionChanges.removeInstance(key, postpone_emit = True)
for key in keys_to_copy:
# Since material_diameter is not on the extruder definition, we need to add it here
# WARNING: this might be very dangerous and should be refactored ASAP!
definition = stack.getSettingDefinition(key)
if definition:
self.definition.addDefinition(definition)
# Only copy the value when this extruder doesn't have the value.
if self.definitionChanges.hasProperty(key, "value"):
continue
setting_value = stack.definitionChanges.getProperty(key, "value")
if setting_value is None:
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":
position = self.getMetaDataEntry("position", "0")
Application.getInstance().getExtruderManager().updateMaterialForDiameter(position)
# 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"]:

View File

@ -1,6 +1,8 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from collections import defaultdict
import threading
from typing import Any, Dict, Optional
from PyQt5.QtCore import pyqtProperty
@ -30,7 +32,8 @@ class GlobalStack(CuraContainerStack):
# This property is used to track which settings we are calculating the "resolve" for
# and if so, to bypass the resolve to prevent an infinite recursion that would occur
# if the resolve function tried to access the same property it is a resolve for.
self._resolving_settings = set()
# Per thread we have our own resolving_settings, or strange things sometimes occur.
self._resolving_settings = defaultdict(set) # keys are thread names
## Get the list of extruders of this stack.
#
@ -91,9 +94,10 @@ class GlobalStack(CuraContainerStack):
# Handle the "resolve" property.
if self._shouldResolve(key, property_name, context):
self._resolving_settings.add(key)
current_thread = threading.current_thread()
self._resolving_settings[current_thread.name].add(key)
resolve = super().getProperty(key, "resolve", context)
self._resolving_settings.remove(key)
self._resolving_settings[current_thread.name].remove(key)
if resolve is not None:
return resolve
@ -145,7 +149,8 @@ class GlobalStack(CuraContainerStack):
# Do not try to resolve anything but the "value" property
return False
if key in self._resolving_settings:
current_thread = threading.current_thread()
if key in self._resolving_settings[current_thread.name]:
# To prevent infinite recursion, if getProperty is called with the same key as
# we are already trying to resolve, we should not try to resolve again. Since
# this can happen multiple times when trying to resolve a value, we need to

View File

@ -1,8 +1,11 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import time
#Type hinting.
from typing import Union, List, Dict
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Signal import Signal
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QTimer
@ -75,6 +78,7 @@ class MachineManager(QObject):
self._stacks_have_errors = None
self._empty_definition_changes_container = ContainerRegistry.getInstance().findContainers(id = "empty_definition_changes")[0]
self._empty_variant_container = ContainerRegistry.getInstance().findContainers(id = "empty_variant")[0]
self._empty_material_container = ContainerRegistry.getInstance().findContainers(id = "empty_material")[0]
self._empty_quality_container = ContainerRegistry.getInstance().findContainers(id = "empty_quality")[0]
@ -404,15 +408,28 @@ class MachineManager(QObject):
Logger.log("w", "Failed creating a new machine!")
def _checkStacksHaveErrors(self) -> bool:
time_start = time.time()
if self._global_container_stack is None: #No active machine.
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))
return True
for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()):
# Not a very pretty solution, but the extruder manager doesn't really know how many extruders there are
machine_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
extruder_stacks = ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())
count = 1 # we start with the global stack
for stack in extruder_stacks:
md = stack.getMetaData()
if "position" in md and int(md["position"]) >= machine_extruder_count:
continue
count += 1
if stack.hasErrors():
Logger.log("d", "Checking %s stacks for errors took %.2f s and we found an error in stack [%s]" % (count, time.time() - time_start, str(stack)))
return True
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)
@ -764,7 +781,7 @@ class MachineManager(QObject):
## 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):
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:
@ -846,10 +863,10 @@ class MachineManager(QObject):
if not old_quality_changes:
new_quality_id = candidate_quality.getId()
self.setActiveQuality(new_quality_id)
self.setActiveQuality(new_quality_id, always_discard_changes = always_discard_changes)
@pyqtSlot(str)
def setActiveVariant(self, variant_id: 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:
@ -865,7 +882,7 @@ class MachineManager(QObject):
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)
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.")
@ -890,10 +907,12 @@ class MachineManager(QObject):
## 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):
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
@ -948,11 +967,13 @@ class MachineManager(QObject):
"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:
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.
@ -960,6 +981,9 @@ class MachineManager(QObject):
# 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
@ -984,6 +1008,8 @@ class MachineManager(QObject):
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):
@ -1339,6 +1365,68 @@ class MachineManager(QObject):
if containers:
return containers[0].definition.getId()
## 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):
extruder_manager = Application.getInstance().getExtruderManager()
definition_changes_container = self._global_container_stack.definitionChanges
if not self._global_container_stack or definition_changes_container == self._empty_definition_changes_container:
return
previous_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
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
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)
# 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()
for node in DepthFirstIterator(root_node):
if node.getMeshData():
extruder_nr = node.callDecoration("getActiveExtruderPosition")
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()
# 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()
for setting_instance in global_user_container.findInstances():
setting_key = setting_instance.definition.key
settable_per_extruder = self._global_container_stack.getProperty(setting_key, "settable_per_extruder")
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"))
global_user_container.removeInstance(setting_key)
# Signal that the global stack has changed
Application.getInstance().globalContainerStackChanged.emit()
@staticmethod
def createMachineManager():
return MachineManager()

View File

@ -24,16 +24,49 @@ 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
i18n_catalog = i18nCatalog("cura")
#
# HACK:
#
# In project loading, when override the existing machine is selected, the stacks and containers that are correctly
# active in the system will be overridden at runtime. Because the project loading is done in a different thread than
# the Qt thread, something else can kick in the middle of the process. One of them is the rendering. It will access
# the current stacks and container, which have not completely been updated yet, so Cura will crash in this case.
#
# This "@call_on_qt_thread" decorator makes sure that a function will always be called on the Qt thread (blocking).
# It is applied to the read() function of project loading so it can be guaranteed that only after the project loading
# process is completely done, everything else that needs to occupy the QT thread will be executed.
#
class InterCallObject:
def __init__(self):
self.finish_event = threading.Event()
self.result = None
def call_on_qt_thread(func):
def _call_on_qt_thread_wrapper(*args, **kwargs):
def _handle_call(ico, *args, **kwargs):
ico.result = func(*args, **kwargs)
ico.finish_event.set()
inter_call_object = InterCallObject()
new_args = tuple([inter_call_object] + list(args)[:])
CuraApplication.getInstance().callLater(_handle_call, *new_args, **kwargs)
inter_call_object.finish_event.wait()
return inter_call_object.result
return _call_on_qt_thread_wrapper
## Base implementation for reading 3MF workspace files.
class ThreeMFWorkspaceReader(WorkspaceReader):
def __init__(self):
@ -400,6 +433,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# containing global.cfg / extruder.cfg
#
# \param file_name
@call_on_qt_thread
def read(self, file_name):
archive = zipfile.ZipFile(file_name, "r")
@ -524,6 +558,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
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")
@ -629,6 +664,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# 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")
@ -684,7 +721,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
stack.setName(global_stack_name_new)
container_stacks_added.append(stack)
self._container_registry.addContainer(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"])
@ -702,6 +739,9 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
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)
@ -750,17 +790,33 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# 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:
stack = self._container_registry.addExtruderStackForSingleExtrusionMachine(global_stack, "fdmextruder")
if stack:
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)
# 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.")
@ -769,6 +825,11 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
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.
@ -801,17 +862,12 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
quality_has_been_changed = False
if has_not_supported:
empty_quality_container = self._container_registry.findInstanceContainers(id = "empty_quality")[0]
for stack in [global_stack] + extruder_stacks_in_use:
stack.replaceContainer(_ContainerIndexes.Quality, empty_quality_container)
empty_quality_changes_container = self._container_registry.findInstanceContainers(id = "empty_quality_changes")[0]
for stack in [global_stack] + extruder_stacks_in_use:
stack.replaceContainer(_ContainerIndexes.QualityChanges, empty_quality_changes_container)
quality_has_been_changed = True
else:
empty_quality_changes_container = self._container_registry.findInstanceContainers(id="empty_quality_changes")[0]
# 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".
@ -922,9 +978,10 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# sanity checks
# NOTE: The following cases SHOULD NOT happen!!!!
if not old_container:
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,
@ -937,26 +994,29 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
continue
# Replace the quality/definition changes container if it's in one of the ExtruderStacks
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 not changes_container:
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())
# 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:
# 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":
each_extruder_stack.qualityChanges = each_changes_container
changes_container = each_extruder_stack.qualityChanges
elif changes_container_type == "definition_changes":
each_extruder_stack.definitionChanges = each_changes_container
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
@ -991,6 +1051,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
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)
@ -1040,13 +1102,13 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# find the old material ID
old_material_id_in_stack = stack.material.getId()
best_matching_old_material_id = None
best_matching_old_meterial_prefix_length = -1
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_meterial_prefix_length:
if len(old_parent_material_id) < best_matching_old_material_prefix_length:
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_meterial_prefix_length = 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:

View File

@ -390,6 +390,13 @@ UM.Dialog
}
}
function accept() {
manager.closeBackend();
manager.onOkButtonClicked();
base.visible = false;
base.accept();
}
function reject() {
manager.onCancelButtonClicked();
base.visible = false;

View File

@ -97,7 +97,7 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
file_in_archive.compress_type = zipfile.ZIP_DEFLATED
# Do not include the network authentication keys
ignore_keys = {"network_authentication_id", "network_authentication_key"}
ignore_keys = {"network_authentication_id", "network_authentication_key", "octoprint_api_key"}
serialized_data = container.serialize(ignored_metadata_keys = ignore_keys)
archive.writestr(file_in_archive, serialized_data)

View File

@ -291,6 +291,7 @@ class CuraEngineBackend(QObject, Backend):
self._start_slice_job = None
if job.isCancelled() or job.getError() or job.getResult() == StartSliceJob.StartJobResult.Error:
self.backendStateChange.emit(BackendState.Error)
return
if job.getResult() == StartSliceJob.StartJobResult.MaterialIncompatible:
@ -426,6 +427,11 @@ class CuraEngineBackend(QObject, Backend):
if not isinstance(source, SceneNode):
return
# This case checks if the source node is a node that contains GCode. In this case the
# current layer data is removed so the previous data is not rendered - CURA-4821
if source.callDecoration("isBlockSlicing") and source.callDecoration("getLayerData"):
self._stored_optimized_layer_data = {}
build_plate_changed = set()
source_build_plate_number = source.callDecoration("getBuildPlateNumber")
if source == self._scene.getRoot():

View File

@ -15,7 +15,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Settings.Validator import ValidatorState
from UM.Settings.SettingRelation import RelationType
from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode
from cura.Scene.CuraSceneNode import CuraSceneNode
from cura.OneAtATimeIterator import OneAtATimeIterator
from cura.Settings.ExtruderManager import ExtruderManager
@ -137,7 +137,7 @@ class StartSliceJob(Job):
# Don't slice if there is a per object setting with an error value.
for node in DepthFirstIterator(self._scene.getRoot()):
if type(node) is not SceneNode or not node.isSelectable():
if type(node) is not CuraSceneNode or not node.isSelectable():
continue
if self._checkStackForErrors(node.callDecoration("getStack")):
@ -161,10 +161,15 @@ class StartSliceJob(Job):
if getattr(node, "_outside_buildarea", False):
continue
# Filter on current build plate
build_plate_number = node.callDecoration("getBuildPlateNumber")
if build_plate_number is not None and build_plate_number != self._build_plate_number:
continue
children = node.getAllChildren()
children.append(node)
for child_node in children:
if type(child_node) is SceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None:
if type(child_node) is CuraSceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None:
temp_list.append(child_node)
if temp_list:
@ -176,7 +181,7 @@ class StartSliceJob(Job):
temp_list = []
has_printing_mesh = False
for node in DepthFirstIterator(self._scene.getRoot()):
if node.callDecoration("isSliceable") and type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None:
if node.callDecoration("isSliceable") and type(node) is CuraSceneNode 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:

View File

@ -49,7 +49,6 @@ class FirmwareUpdateChecker(Extension):
def _onContainerAdded(self, container):
# Only take care when a new GlobalStack was added
if isinstance(container, GlobalStack):
Logger.log("i", "You have a '%s' in printer list. Let's check the firmware!", container.getId())
self.checkFirmwareVersion(container, True)
## Connect with software.ultimaker.com, load latest.version and check version info.

View File

@ -44,6 +44,8 @@ class FirmwareUpdateCheckerJob(Job):
# Now we just do that if the active printer is Ultimaker 3 or Ultimaker 3 Extended or any
# other Ultimaker 3 that will come in the future
if len(machine_name_parts) >= 2 and machine_name_parts[:2] == ["ultimaker", "3"]:
Logger.log("i", "You have a UM3 in printer list. Let's check the firmware!")
# Nothing to parse, just get the string
# TODO: In the future may be done by parsing a JSON file with diferent version for each printer model
current_version = reader(current_version_file).readline().rstrip()

View File

@ -107,7 +107,7 @@ class GCodeWriter(MeshWriter):
prefix_length = len(prefix)
container_with_profile = stack.qualityChanges
if not container_with_profile:
if container_with_profile.getId() == "empty_quality_changes":
Logger.log("e", "No valid quality profile found, not writing settings to GCode!")
return ""
@ -123,9 +123,9 @@ class GCodeWriter(MeshWriter):
serialized = flat_global_container.serialize()
data = {"global_quality": serialized}
for extruder in sorted(ExtruderManager.getInstance().getMachineExtruders(stack.getId()), key = lambda k: k.getMetaDataEntry("position")):
for extruder in sorted(stack.extruders.values(), key = lambda k: k.getMetaDataEntry("position")):
extruder_quality = extruder.qualityChanges
if not extruder_quality:
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)

View File

@ -105,60 +105,9 @@ class MachineSettingsAction(MachineAction):
@pyqtSlot(int)
def setMachineExtruderCount(self, extruder_count):
extruder_manager = Application.getInstance().getExtruderManager()
definition_changes_container = self._global_container_stack.definitionChanges
if not self._global_container_stack or definition_changes_container == self._empty_container:
return
previous_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
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
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)
# 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()
for node in DepthFirstIterator(root_node):
if node.getMeshData():
extruder_nr = node.callDecoration("getActiveExtruderPosition")
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()
if previous_extruder_count == 1:
extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
global_user_container = self._global_container_stack.getTop()
for setting_instance in global_user_container.findInstances():
setting_key = setting_instance.definition.key
settable_per_extruder = self._global_container_stack.getProperty(setting_key, "settable_per_extruder")
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"))
global_user_container.removeInstance(setting_key)
self.forceUpdate()
# 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)
@pyqtSlot()
def forceUpdate(self):
@ -209,79 +158,4 @@ class MachineSettingsAction(MachineAction):
@pyqtSlot(int)
def updateMaterialForDiameter(self, extruder_position: int):
# Updates the material container to a material that matches the material diameter set for the printer
if not self._global_container_stack:
return
if not self._global_container_stack.getMetaDataEntry("has_materials", False):
return
extruder_stack = self._global_container_stack.extruders[str(extruder_position)]
material_diameter = extruder_stack.material.getProperty("material_diameter", "value")
if not material_diameter:
# in case of "empty" material
material_diameter = 0
material_approximate_diameter = str(round(material_diameter))
machine_diameter = extruder_stack.definitionChanges.getProperty("material_diameter", "value")
if not machine_diameter:
if extruder_stack.definition.hasProperty("material_diameter", "value"):
machine_diameter = extruder_stack.definition.getProperty("material_diameter", "value")
else:
machine_diameter = self._global_container_stack.definition.getProperty("material_diameter", "value")
machine_approximate_diameter = str(round(machine_diameter))
if material_approximate_diameter != machine_approximate_diameter:
Logger.log("i", "The the currently active material(s) do not match the diameter set for the printer. Finding alternatives.")
if self._global_container_stack.getMetaDataEntry("has_machine_materials", False):
materials_definition = self._global_container_stack.definition.getId()
has_material_variants = self._global_container_stack.getMetaDataEntry("has_variants", False)
else:
materials_definition = "fdmprinter"
has_material_variants = False
old_material = extruder_stack.material
search_criteria = {
"type": "material",
"approximate_diameter": machine_approximate_diameter,
"material": old_material.getMetaDataEntry("material", "value"),
"brand": old_material.getMetaDataEntry("brand", "value"),
"supplier": old_material.getMetaDataEntry("supplier", "value"),
"color_name": old_material.getMetaDataEntry("color_name", "value"),
"definition": materials_definition
}
if has_material_variants:
search_criteria["variant"] = extruder_stack.variant.getId()
if old_material == self._empty_container:
search_criteria.pop("material", None)
search_criteria.pop("supplier", None)
search_criteria.pop("brand", None)
search_criteria.pop("definition", None)
search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material")
materials = self._container_registry.findInstanceContainers(**search_criteria)
if not materials:
# Same material with new diameter is not found, search for generic version of the same material type
search_criteria.pop("supplier", None)
search_criteria.pop("brand", None)
search_criteria["color_name"] = "Generic"
materials = self._container_registry.findInstanceContainers(**search_criteria)
if not materials:
# Generic material with new diameter is not found, search for preferred material
search_criteria.pop("color_name", None)
search_criteria.pop("material", None)
search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material")
materials = self._container_registry.findInstanceContainers(**search_criteria)
if not materials:
# Preferred material with new diameter is not found, search for any material
search_criteria.pop("id", None)
materials = self._container_registry.findInstanceContainers(**search_criteria)
if not materials:
# Just use empty material as a final fallback
materials = [self._empty_container]
Logger.log("i", "Selecting new material: %s", materials[0].getId())
extruder_stack.material = materials[0]
Application.getInstance().getExtruderManager().updateMaterialForDiameter(extruder_position)

View File

@ -390,7 +390,7 @@ Cura.MachineAction
visible: Cura.MachineManager.hasMaterials
sourceComponent: numericTextFieldWithUnit
property string settingKey: "material_diameter"
property string label: catalog.i18nc("@label", "Material diameter")
property string label: catalog.i18nc("@label", "Compatible material diameter")
property string unit: catalog.i18nc("@label", "mm")
property string tooltip: catalog.i18nc("@tooltip", "The nominal diameter of filament supported by the printer. The exact diameter will be overridden by the material and/or the profile.")
function afterOnEditingFinished()

View File

@ -25,7 +25,7 @@ class PluginBrowser(QObject, Extension):
def __init__(self, parent=None):
super().__init__(parent)
self._api_version = 2
self._api_version = 4
self._api_url = "http://software.ultimaker.com/cura/v%s/" % self._api_version
self._plugin_list_request = None

View File

@ -98,11 +98,14 @@ class SimulationView(View):
self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count"))
self._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers"))
self._compatibility_mode = True # for safety
self._compatibility_mode = self._evaluateCompatibilityMode()
self._wireprint_warning_message = Message(catalog.i18nc("@info:status", "Cura does not accurately display layers when Wire Printing is enabled"),
title = catalog.i18nc("@info:title", "Simulation View"))
def _evaluateCompatibilityMode(self):
return OpenGLContext.isLegacyOpenGL() or bool(Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode"))
def _resetSettings(self):
self._layer_view_type = 0 # 0 is material color, 1 is color by linetype, 2 is speed, 3 is layer thickness
self._extruder_count = 0
@ -127,7 +130,7 @@ class SimulationView(View):
# Currently the RenderPass constructor requires a size > 0
# This should be fixed in RenderPass's constructor.
self._layer_pass = SimulationPass(1, 1)
self._compatibility_mode = OpenGLContext.isLegacyOpenGL() or bool(Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode"))
self._compatibility_mode = self._evaluateCompatibilityMode()
self._layer_pass.setSimulationView(self)
return self._layer_pass
@ -534,8 +537,7 @@ class SimulationView(View):
def _updateWithPreferences(self):
self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count"))
self._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers"))
self._compatibility_mode = OpenGLContext.isLegacyOpenGL() or bool(
Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode"))
self._compatibility_mode = self._evaluateCompatibilityMode()
self.setSimulationViewType(int(float(Preferences.getInstance().getValue("layerview/layer_view_type"))));

View File

@ -485,7 +485,7 @@ Item
}
}
// Gradient colors for layer thickness
// Gradient colors for layer thickness (similar to parula colormap)
Rectangle { // In QML 5.9 can be changed by LinearGradient
// Invert values because then the bar is rotated 90 degrees
id: thicknessGradient
@ -499,23 +499,23 @@ Item
gradient: Gradient {
GradientStop {
position: 0.000
color: Qt.rgba(1, 0, 0, 1)
color: Qt.rgba(1, 1, 0, 1)
}
GradientStop {
position: 0.25
color: Qt.rgba(0.5, 0.5, 0, 1)
color: Qt.rgba(1, 0.75, 0.25, 1)
}
GradientStop {
position: 0.5
color: Qt.rgba(0, 1, 0, 1)
color: Qt.rgba(0, 0.75, 0.5, 1)
}
GradientStop {
position: 0.75
color: Qt.rgba(0, 0.5, 0.5, 1)
color: Qt.rgba(0, 0.375, 0.75, 1)
}
GradientStop {
position: 1.0
color: Qt.rgba(0, 0, 1, 1)
color: Qt.rgba(0, 0, 0.5, 1)
}
}
}

View File

@ -54,9 +54,13 @@ vertex41core =
vec4 layerThicknessGradientColor(float abs_value, float min_value, float max_value)
{
float value = (abs_value - min_value)/(max_value - min_value);
float red = max(2*value-1, 0);
float green = 1-abs(1-2*value);
float blue = max(1-2*value, 0);
float red = min(max(4*value-2, 0), 1);
float green = min(1.5*value, 0.75);
if (value > 0.75)
{
green = value;
}
float blue = 0.75-abs(0.25-value);
return vec4(red, green, blue, 1.0);
}

View File

@ -107,7 +107,7 @@ class SliceInfo(Extension):
"brand": extruder.material.getMetaData().get("brand", "")
}
extruder_position = int(extruder.getMetaDataEntry("position", "0"))
if extruder_position in print_information.materialLengths:
if len(print_information.materialLengths) > extruder_position:
extruder_dict["material_used"] = print_information.materialLengths[extruder_position]
extruder_dict["variant"] = extruder.variant.getName()
extruder_dict["nozzle_size"] = extruder.getProperty("machine_nozzle_size", "value")

View File

@ -0,0 +1,68 @@
import zipfile
from io import StringIO
from UM.Resources import Resources
from UM.Mesh.MeshWriter import MeshWriter
from UM.Logger import Logger
from UM.PluginRegistry import PluginRegistry
MYPY = False
try:
if not MYPY:
import xml.etree.cElementTree as ET
except ImportError:
Logger.log("w", "Unable to load cElementTree, switching to slower version")
import xml.etree.ElementTree as ET
class UCPWriter(MeshWriter):
def __init__(self):
super().__init__()
self._namespaces = {
"content-types": "http://schemas.openxmlformats.org/package/2006/content-types",
"relationships": "http://schemas.openxmlformats.org/package/2006/relationships",
}
def write(self, stream, nodes, mode = MeshWriter.OutputMode.BinaryMode):
self._archive = None # Reset archive
archive = zipfile.ZipFile(stream, "w", compression=zipfile.ZIP_DEFLATED)
gcode_file = zipfile.ZipInfo("3D/model.gcode")
gcode_file.compress_type = zipfile.ZIP_DEFLATED
# Create content types file
content_types_file = zipfile.ZipInfo("[Content_Types].xml")
content_types_file.compress_type = zipfile.ZIP_DEFLATED
content_types = ET.Element("Types", xmlns=self._namespaces["content-types"])
rels_type = ET.SubElement(content_types, "Default", Extension="rels",
ContentType="application/vnd.openxmlformats-package.relationships+xml")
gcode_type = ET.SubElement(content_types, "Default", Extension="gcode",
ContentType="text/x-gcode")
image_type = ET.SubElement(content_types, "Default", Extension="png",
ContentType="image/png")
# Create _rels/.rels file
relations_file = zipfile.ZipInfo("_rels/.rels")
relations_file.compress_type = zipfile.ZIP_DEFLATED
relations_element = ET.Element("Relationships", xmlns=self._namespaces["relationships"])
thumbnail_relation_element = ET.SubElement(relations_element, "Relationship", Target="/Metadata/thumbnail.png", Id="rel0",
Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail")
model_relation_element = ET.SubElement(relations_element, "Relationship", Target="/3D/model.gcode",
Id="rel1",
Type="http://schemas.ultimaker.org/package/2018/relationships/gcode")
gcode_string = StringIO()
PluginRegistry.getInstance().getPluginObject("GCodeWriter").write(gcode_string, None)
archive.write(Resources.getPath(Resources.Images, "cura-icon.png"), "Metadata/thumbnail.png")
archive.writestr(gcode_file, gcode_string.getvalue())
archive.writestr(content_types_file, b'<?xml version="1.0" encoding="UTF-8"?> \n' + ET.tostring(content_types))
archive.writestr(relations_file, b'<?xml version="1.0" encoding="UTF-8"?> \n' + ET.tostring(relations_element))
archive.close()

View File

@ -0,0 +1,25 @@
# Copyright (c) 2017 Ultimaker B.V.
# Uranium is released under the terms of the LGPLv3 or higher.
from . import UCPWriter
from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("cura")
def getMetaData():
return {
"mesh_writer": {
"output": [
{
"mime_type": "application/x-ucp",
"mode": UCPWriter.UCPWriter.OutputMode.BinaryMode,
"extension": "UCP",
"description": i18n_catalog.i18nc("@item:inlistbox", "UCP File (WIP)")
}
]
}
}
def register(app):
return { "mesh_writer": UCPWriter.UCPWriter() }

View File

@ -0,0 +1,8 @@
{
"name": "UCP Writer",
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Provides support for writing UCP files.",
"api": 4,
"i18n-catalog": "cura"
}

View File

@ -287,7 +287,11 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._updatePrintJob(print_job, print_job_data)
if print_job.state != "queued": # Print job should be assigned to a printer.
printer = self._getPrinterByKey(print_job_data["printer_uuid"])
if print_job.state == "failed":
# Print job was failed, so don't attach it to a printer.
printer = None
else:
printer = self._getPrinterByKey(print_job_data["printer_uuid"])
else: # The job can "reserve" a printer if some changes are required.
printer = self._getPrinterByKey(print_job_data["assigned_to"])

View File

@ -541,7 +541,6 @@ class XmlMaterialProfile(InstanceContainer):
Logger.log("w", "No definition found for machine ID %s", machine_id)
continue
Logger.log("d", "Found definition for machine ID %s", machine_id)
definition = definitions[0]
machine_manufacturer = identifier.get("manufacturer", definition.get("manufacturer", "Unknown")) #If the XML material doesn't specify a manufacturer, use the one in the actual printer definition.

View File

@ -668,20 +668,6 @@
"settable_per_mesh": false,
"settable_per_extruder": false
},
"slicing_tolerance":
{
"label": "Slicing Tolerance",
"description": "How to slice layers with diagonal surfaces. The areas of a layer can be generated based on where the middle of the layer intersects the surface (Middle). Alternatively each layer can have the areas which fall inside of the volume throughout the height of the layer (Exclusive) or a layer has the areas which fall inside anywhere within the layer (Inclusive). Exclusive retains the most details, Inclusive makes for the best fit and Middle takes the least time to process.",
"type": "enum",
"options":
{
"middle": "Middle",
"exclusive": "Exclusive",
"inclusive": "Inclusive"
},
"default_value": "middle",
"settable_per_mesh": true
},
"line_width":
{
"label": "Line Width",
@ -741,21 +727,6 @@
}
}
},
"roofing_line_width":
{
"label": "Top Surface Skin Line Width",
"description": "Width of a single line of the areas at the top of the print.",
"unit": "mm",
"minimum_value": "0.001",
"minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size",
"maximum_value_warning": "2 * machine_nozzle_size",
"default_value": 0.4,
"type": "float",
"value": "skin_line_width",
"limit_to_extruder": "roofing_extruder_nr",
"settable_per_mesh": true,
"enabled": "roofing_layer_count > 0 and top_layers > 0"
},
"skin_line_width":
{
"label": "Top/Bottom Line Width",
@ -1014,34 +985,6 @@
"settable_per_mesh": true,
"enabled": "top_layers > 0"
},
"roofing_pattern":
{
"label": "Top Surface Skin Pattern",
"description": "The pattern of the top most layers.",
"type": "enum",
"options":
{
"lines": "Lines",
"concentric": "Concentric",
"zigzag": "Zig Zag"
},
"default_value": "lines",
"value": "top_bottom_pattern",
"limit_to_extruder": "roofing_extruder_nr",
"settable_per_mesh": true,
"enabled": "roofing_layer_count > 0 and top_layers > 0"
},
"roofing_angles":
{
"label": "Top Surface Skin Line Directions",
"description": "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).",
"type": "[int]",
"default_value": "[ ]",
"value": "skin_angles",
"enabled": "roofing_pattern != 'concentric' and roofing_layer_count > 0 and top_layers > 0",
"limit_to_extruder": "roofing_extruder_nr",
"settable_per_mesh": true
},
"top_bottom_extruder_nr":
{
"label": "Top/Bottom Extruder",
@ -1183,6 +1126,14 @@
"limit_to_extruder": "wall_0_extruder_nr",
"settable_per_mesh": true
},
"optimize_wall_printing_order":
{
"label": "Optimize Wall Printing Order",
"description": "Optimize the order in which walls are printed so as to reduce the number of retractions and the distance travelled. Most parts will benefit from this being enabled but some may actually take longer so please compare the print time estimates with and without optimization.",
"type": "bool",
"default_value": false,
"settable_per_mesh": true
},
"outer_inset_first":
{
"label": "Outer Before Inner Walls",
@ -1868,14 +1819,6 @@
"settable_per_mesh": true
}
}
},
"infill_enable_travel_optimization":
{
"label": "Enable Travel Optimization",
"description": "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.",
"type": "bool",
"default_value": false,
"settable_per_mesh": true
}
}
},
@ -1887,16 +1830,6 @@
"type": "category",
"children":
{
"material_flow_dependent_temperature":
{
"label": "Auto Temperature",
"description": "Change the temperature for each layer automatically with the average flow speed of that layer.",
"type": "bool",
"default_value": false,
"enabled": "machine_nozzle_temp_enabled and False",
"settable_per_mesh": false,
"settable_per_extruder": true
},
"default_material_print_temperature":
{
"label": "Default Printing Temperature",
@ -1971,17 +1904,6 @@
"settable_per_mesh": false,
"settable_per_extruder": true
},
"material_flow_temp_graph":
{
"label": "Flow Temperature Graph",
"description": "Data linking material flow (in mm3 per second) to temperature (degrees Celsius).",
"unit": "[[mm³,°C]]",
"type": "str",
"default_value": "[[3.5,200],[7.0,240]]",
"enabled": "False and machine_nozzle_temp_enabled and material_flow_dependent_temperature",
"settable_per_mesh": false,
"settable_per_extruder": true
},
"material_extrusion_cool_down_speed":
{
"label": "Extrusion Cool Down Speed Modifier",
@ -2097,6 +2019,19 @@
"enabled": "machine_gcode_flavor != \"UltiGCode\"",
"settable_per_mesh": true
},
"material_flow_layer_0":
{
"label": "Initial Layer Flow",
"description": "Flow compensation for the first layer: the amount of material extruded on the initial layer is multiplied by this value.",
"unit": "%",
"default_value": 100,
"value": "material_flow",
"type": "float",
"minimum_value": "0.0001",
"minimum_value_warning": "50",
"maximum_value_warning": "150",
"settable_per_mesh": true
},
"retraction_enable":
{
"label": "Enable Retraction",
@ -2106,7 +2041,8 @@
"settable_per_mesh": false,
"settable_per_extruder": true
},
"retract_at_layer_change":{
"retract_at_layer_change":
{
"label": "Retract at Layer Change",
"description": "Retract the filament when the nozzle is moving to the next layer.",
"type": "bool",
@ -3514,15 +3450,6 @@
"settable_per_mesh": true,
"settable_per_extruder": false
},
"support_tree_enable":
{
"label": "Tree Support",
"description": "Generate a tree-like support with branches that support your print. This may reduce material usage and print time.",
"type": "bool",
"default_value": false,
"settable_per_mesh": true,
"settable_per_extruder": false
},
"support_extruder_nr":
{
"label": "Support Extruder",
@ -3841,110 +3768,6 @@
"limit_to_extruder": "support_infill_extruder_nr",
"settable_per_mesh": false
},
"support_tree_angle":
{
"label": "Tree Support Branch Angle",
"description": "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.",
"unit": "°",
"type": "float",
"minimum_value": "0",
"maximum_value": "90",
"maximum_value_warning": "60",
"default_value": 40,
"limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_tree_enable",
"settable_per_mesh": false,
"settable_per_extruder": true
},
"support_tree_branch_distance":
{
"label": "Tree Support Branch Distance",
"description": "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.",
"unit": "mm",
"type": "float",
"minimum_value": "0.001",
"default_value": 4,
"limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_tree_enable",
"settable_per_mesh": true
},
"support_tree_branch_diameter":
{
"label": "Tree Support Branch Diameter",
"description": "The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this.",
"unit": "mm",
"type": "float",
"minimum_value": "0.001",
"minimum_value_warning": "support_line_width * 2",
"default_value": 2,
"limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_tree_enable",
"settable_per_mesh": false,
"settable_per_extruder": true
},
"support_tree_branch_diameter_angle":
{
"label": "Tree Support Branch Diameter Angle",
"description": "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.",
"unit": "°",
"type": "float",
"minimum_value": "0",
"maximum_value": "89.9999",
"maximum_value_warning": "15",
"default_value": 5,
"limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_tree_enable",
"settable_per_mesh": false,
"settable_per_extruder": true
},
"support_tree_collision_resolution":
{
"label": "Tree Support Collision Resolution",
"description": "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.",
"unit": "mm",
"type": "float",
"minimum_value": "0.001",
"minimum_value_warning": "support_line_width / 4",
"maximum_value_warning": "support_line_width * 2",
"default_value": 0.4,
"value": "support_line_width / 2",
"limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_tree_enable and support_tree_branch_diameter_angle > 0",
"settable_per_mesh": false,
"settable_per_extruder": true
},
"support_tree_wall_thickness":
{
"label": "Tree Support Wall Thickness",
"description": "The thickness of the walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily.",
"unit": "mm",
"type": "float",
"minimum_value": "0",
"minimum_value_warning": "wall_line_width",
"default_value": 0.8,
"value": "support_line_width",
"limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_tree_enable",
"settable_per_mesh": false,
"settable_per_extruder": true,
"children":
{
"support_tree_wall_count":
{
"label": "Tree Support Wall Line Count",
"description": "The number of walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily.",
"type": "int",
"minimum_value": "0",
"minimum_value_warning": "1",
"default_value": 1,
"value": "round(support_tree_wall_thickness / support_line_width)",
"limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_tree_enable",
"settable_per_mesh": false,
"settable_per_extruder": true
}
}
},
"gradual_support_infill_steps":
{
"label": "Gradual Support Infill Steps",
@ -5129,18 +4952,6 @@
"default_value": false,
"settable_per_mesh": true
},
"meshfix_maximum_resolution":
{
"label": "Maximum Resolution",
"description": "The minimum size of a line segment after slicing. If you increase this, the mesh will have a lower resolution. This may allow the printer to keep up with the speed it has to process g-code and will increase slice speed by removing details of the mesh that it can't process anyway.",
"type": "float",
"unit": "mm",
"default_value": 0.01,
"minimum_value": "0.001",
"minimum_value_warning": "0.005",
"maximum_value_warning": "0.1",
"settable_per_mesh": true
},
"multiple_mesh_overlap":
{
"label": "Merged Meshes Overlap",
@ -5369,12 +5180,215 @@
"description": "experimental!",
"children":
{
"optimize_wall_printing_order":
"support_tree_enable":
{
"label": "Optimize Wall Printing Order",
"description": "Optimize the order in which walls are printed so as to reduce the number of retractions and the distance travelled. Most parts will benefit from this being enabled but some may actually take longer so please compare the print time estimates with and without optimization.",
"label": "Tree Support",
"description": "Generate a tree-like support with branches that support your print. This may reduce material usage and print time, but greatly increases slicing time.",
"type": "bool",
"default_value": false,
"settable_per_mesh": true,
"settable_per_extruder": false
},
"support_tree_angle":
{
"label": "Tree Support Branch Angle",
"description": "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.",
"unit": "°",
"type": "float",
"minimum_value": "0",
"maximum_value": "90",
"maximum_value_warning": "60",
"default_value": 40,
"limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_tree_enable",
"settable_per_mesh": false,
"settable_per_extruder": true
},
"support_tree_branch_distance":
{
"label": "Tree Support Branch Distance",
"description": "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.",
"unit": "mm",
"type": "float",
"minimum_value": "0.001",
"default_value": 4,
"limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_tree_enable",
"settable_per_mesh": true
},
"support_tree_branch_diameter":
{
"label": "Tree Support Branch Diameter",
"description": "The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this.",
"unit": "mm",
"type": "float",
"minimum_value": "0.001",
"minimum_value_warning": "support_line_width * 2",
"default_value": 2,
"limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_tree_enable",
"settable_per_mesh": false,
"settable_per_extruder": true
},
"support_tree_branch_diameter_angle":
{
"label": "Tree Support Branch Diameter Angle",
"description": "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.",
"unit": "°",
"type": "float",
"minimum_value": "0",
"maximum_value": "89.9999",
"maximum_value_warning": "15",
"default_value": 5,
"limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_tree_enable",
"settable_per_mesh": false,
"settable_per_extruder": true
},
"support_tree_collision_resolution":
{
"label": "Tree Support Collision Resolution",
"description": "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.",
"unit": "mm",
"type": "float",
"minimum_value": "0.001",
"minimum_value_warning": "support_line_width / 4",
"maximum_value_warning": "support_line_width * 2",
"default_value": 0.4,
"value": "support_line_width / 2",
"limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_tree_enable and support_tree_branch_diameter_angle > 0",
"settable_per_mesh": false,
"settable_per_extruder": true
},
"support_tree_wall_thickness":
{
"label": "Tree Support Wall Thickness",
"description": "The thickness of the walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily.",
"unit": "mm",
"type": "float",
"minimum_value": "0",
"minimum_value_warning": "wall_line_width",
"default_value": 0.8,
"value": "support_line_width",
"limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_tree_enable",
"settable_per_mesh": false,
"settable_per_extruder": true,
"children":
{
"support_tree_wall_count":
{
"label": "Tree Support Wall Line Count",
"description": "The number of walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily.",
"type": "int",
"minimum_value": "0",
"minimum_value_warning": "1",
"default_value": 1,
"value": "round(support_tree_wall_thickness / support_line_width)",
"limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_tree_enable",
"settable_per_mesh": false,
"settable_per_extruder": true
}
}
},
"slicing_tolerance":
{
"label": "Slicing Tolerance",
"description": "How to slice layers with diagonal surfaces. The areas of a layer can be generated based on where the middle of the layer intersects the surface (Middle). Alternatively each layer can have the areas which fall inside of the volume throughout the height of the layer (Exclusive) or a layer has the areas which fall inside anywhere within the layer (Inclusive). Exclusive retains the most details, Inclusive makes for the best fit and Middle takes the least time to process.",
"type": "enum",
"options":
{
"middle": "Middle",
"exclusive": "Exclusive",
"inclusive": "Inclusive"
},
"default_value": "middle",
"settable_per_mesh": true
},
"roofing_line_width":
{
"label": "Top Surface Skin Line Width",
"description": "Width of a single line of the areas at the top of the print.",
"unit": "mm",
"minimum_value": "0.001",
"minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size",
"maximum_value_warning": "2 * machine_nozzle_size",
"default_value": 0.4,
"type": "float",
"value": "skin_line_width",
"limit_to_extruder": "roofing_extruder_nr",
"settable_per_mesh": true,
"enabled": "roofing_layer_count > 0 and top_layers > 0"
},
"roofing_pattern":
{
"label": "Top Surface Skin Pattern",
"description": "The pattern of the top most layers.",
"type": "enum",
"options":
{
"lines": "Lines",
"concentric": "Concentric",
"zigzag": "Zig Zag"
},
"default_value": "lines",
"value": "top_bottom_pattern",
"limit_to_extruder": "roofing_extruder_nr",
"settable_per_mesh": true,
"enabled": "roofing_layer_count > 0 and top_layers > 0"
},
"roofing_angles":
{
"label": "Top Surface Skin Line Directions",
"description": "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).",
"type": "[int]",
"default_value": "[ ]",
"value": "skin_angles",
"enabled": "roofing_pattern != 'concentric' and roofing_layer_count > 0 and top_layers > 0",
"limit_to_extruder": "roofing_extruder_nr",
"settable_per_mesh": true
},
"infill_enable_travel_optimization":
{
"label": "Infill Travel Optimization",
"description": "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.",
"type": "bool",
"default_value": false,
"settable_per_mesh": true
},
"material_flow_dependent_temperature":
{
"label": "Auto Temperature",
"description": "Change the temperature for each layer automatically with the average flow speed of that layer.",
"type": "bool",
"default_value": false,
"enabled": "machine_nozzle_temp_enabled and False",
"settable_per_mesh": false,
"settable_per_extruder": true
},
"material_flow_temp_graph":
{
"label": "Flow Temperature Graph",
"description": "Data linking material flow (in mm3 per second) to temperature (degrees Celsius).",
"unit": "[[mm³,°C]]",
"type": "str",
"default_value": "[[3.5,200],[7.0,240]]",
"enabled": "False and machine_nozzle_temp_enabled and material_flow_dependent_temperature",
"settable_per_mesh": false,
"settable_per_extruder": true
},
"meshfix_maximum_resolution":
{
"label": "Maximum Resolution",
"description": "The minimum size of a line segment after slicing. If you increase this, the mesh will have a lower resolution. This may allow the printer to keep up with the speed it has to process g-code and will increase slice speed by removing details of the mesh that it can't process anyway.",
"type": "float",
"unit": "mm",
"default_value": 0.01,
"minimum_value": "0.001",
"minimum_value_warning": "0.005",
"maximum_value_warning": "0.1",
"settable_per_mesh": true
},
"support_skip_some_zags":

View File

@ -0,0 +1,53 @@
{
"id": "gmax15plus",
"version": 2,
"name": "gMax 1.5 Plus",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "gcreate",
"manufacturer": "gcreate",
"category": "Other",
"file_formats": "text/x-gcode",
"platform": "gmax_1-5_xt-plus_s3d_full model_150707.stl",
"has_variants": true,
"variants_name": "Hotend",
"preferred_variant": "*0.5mm E3D (Default)*"
},
"overrides": {
"machine_extruder_count": { "default_value": 1 },
"machine_name": { "default_value": "gMax 1.5 Plus" },
"machine_heated_bed": { "default_value": false },
"machine_width": { "default_value": 406 },
"machine_depth": { "default_value": 406 },
"machine_height": { "default_value": 533 },
"machine_center_is_zero": { "default_value": false },
"material_diameter": { "default_value": 1.75 },
"machine_nozzle_size": { "default_value": 0.5 },
"layer_height": { "default_value": 0.2 },
"layer_height_0": { "default_value": 0.3 },
"retraction_amount": { "default_value": 1 },
"retraction_speed": { "default_value": 70},
"adhesion_type": { "default_value": "skirt" },
"gantry_height": { "default_value": 50 },
"speed_print": { "default_value": 50 },
"speed_travel": { "default_value": 70 },
"machine_max_acceleration_x": { "default_value": 600 },
"machine_max_acceleration_y": { "default_value": 600 },
"machine_max_acceleration_z": { "default_value": 30 },
"machine_max_acceleration_e": { "default_value": 10000 },
"machine_max_jerk_xy": { "default_value": 8 },
"machine_max_jerk_z": { "default_value": 0.4 },
"machine_max_jerk_e": { "default_value": 5.0 },
"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 ;move X/Y to min endstops\nG28 ;Home X/Y/Z\nG29 ; Bed level\nM104 S{material_print_temperature} ; Preheat\nM109 S{material_print_temperature} ; Preheat\nG91 ;relative positioning\nG90 ;absolute positioning\nG1 Z25.0 F9000 ;raise nozzle 25mm\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\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning" },
"material_print_temperature": { "default_value": 202 },
"wall_thickness": { "default_value": 1 },
"top_bottom_thickness": { "default_value": 1 },
"bottom_thickness": { "default_value": 1 }
}
}

View File

@ -0,0 +1,55 @@
{
"id": "gmax15plus_dual",
"version": 2,
"name": "gMax 1.5 Plus Dual Extruder",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "GTL_180109",
"manufacturer": "gCreate",
"category": "Other",
"file_formats": "text/x-gcode",
"platform": "gmax_1-5_xt-plus_s3d_full model_150707.stl",
"has_variants": true,
"variants_name": "Hotend",
"preferred_variant": "*0.5mm E3D (Default)*",
"machine_extruder_trains": {
"0": "gmax15plus_dual_extruder_0",
"1": "gmax15plus_dual_extruder_1"
}
},
"overrides": {
"machine_name": { "default_value": "gMax 1.5 Plus Dual Extruder" },
"machine_extruder_count": { "default_value": 2 },
"machine_heated_bed": { "default_value": false },
"machine_width": { "default_value": 406 },
"machine_depth": { "default_value": 406 },
"machine_height": { "default_value": 533 },
"machine_center_is_zero": { "default_value": false },
"material_diameter": { "default_value": 1.75 },
"machine_nozzle_size": { "default_value": 0.5 },
"layer_height": { "default_value": 0.2 },
"layer_height_0": { "default_value": 0.3 },
"retraction_amount": { "default_value": 1 },
"retraction_speed": { "default_value": 70},
"adhesion_type": { "default_value": "skirt" },
"gantry_height": { "default_value": 50 },
"speed_print": { "default_value": 50 },
"speed_travel": { "default_value": 70 },
"machine_max_acceleration_x": { "default_value": 600 },
"machine_max_acceleration_y": { "default_value": 600 },
"machine_max_acceleration_z": { "default_value": 30 },
"machine_max_acceleration_e": { "default_value": 10000 },
"machine_max_jerk_xy": { "default_value": 8 },
"machine_max_jerk_z": { "default_value": 0.4 },
"machine_max_jerk_e": { "default_value": 5.0 },
"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 ;move X/Y to min endstops\nG28 ;Home X/Y/Z\nG29 ; Bed level\nM104 S{material_print_temperature} T0 ; Preheat Left Extruder\nM104 S{material_print_temperature} T1 ; Preheat Right Extruder\nM109 S{material_print_temperature} T0 ; Preheat Left Extruder\nM109 S{material_print_temperature} T1 ; Preheat Right Extruder\nG91 ;relative positioning\nG90 ;absolute positioning\nM218 T1 X34.3 Y0; Set 2nd extruder offset. This can be changed later if needed\nG1 Z25.0 F9000 ;raise nozzle 25mm\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 T0;Left extruder off\nM104 S0 T1; Right extruder 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" },
"material_print_temperature": { "default_value": 202 },
"wall_thickness": { "default_value": 1 },
"top_bottom_thickness": { "default_value": 1 },
"bottom_thickness": { "default_value": 1 }
}
}

View File

@ -0,0 +1,28 @@
{
"id": "gmax15plus_dual_extruder_0",
"version": 2,
"name": "Left Extruder",
"inherits": "fdmextruder",
"metadata": {
"machine": "gmax15plus_dual",
"position": "0"
},
"overrides": {
"extruder_nr": {
"default_value": 0,
"maximum_value": "1"
},
"machine_nozzle_offset_x": { "default_value": 0.0 },
"machine_nozzle_offset_y": { "default_value": 0.0 },
"machine_nozzle_size": { "default_value": 0.5 },
"machine_extruder_start_pos_abs": { "default_value": true },
"machine_extruder_start_pos_x": { "value": 40 },
"machine_extruder_start_pos_y": { "value": 210 },
"machine_extruder_end_pos_abs": { "default_value": true },
"machine_extruder_end_pos_x": { "value": 40 },
"machine_extruder_end_pos_y": { "value": 210 }
}
}

View File

@ -0,0 +1,30 @@
{
"id": "gmax15plus_dual_extruder_1",
"version": 2,
"name": "Right Extruder",
"inherits": "fdmextruder",
"metadata": {
"machine": "gmax15plus_dual",
"position": "1"
},
"overrides": {
"extruder_nr": {
"default_value": 1,
"maximum_value": "1"
},
"machine_nozzle_offset_x": { "default_value": 0.0 },
"machine_nozzle_offset_y": { "default_value": 0.0 },
"machine_nozzle_size": { "default_value": 0.5 },
"machine_extruder_start_pos_abs": { "default_value": true },
"machine_extruder_start_pos_x": { "value": 40 },
"machine_extruder_start_pos_y": { "value": 210 },
"machine_extruder_end_pos_abs": { "default_value": true },
"machine_extruder_end_pos_x": { "value": 40 },
"machine_extruder_end_pos_y": { "value": 210 }
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2,20 +2,21 @@
# Copyright (C) 2017 Ultimaker
# This file is distributed under the same license as the Cura package.
# Ruben Dulek <r.dulek@ultimaker.com>, 2017.
#
#
msgid ""
msgstr ""
"Project-Id-Version: Cura 3.1\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-07 13:41+0100\n"
"Last-Translator: Bothof <info@bothof.nl>\n"
"PO-Revision-Date: 2018-01-23 19:35+0000\n"
"Last-Translator: Paulo Miranda <av@utopica3d.com>\n"
"Language-Team: Bothof\n"
"Language: pt_PT\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.5\n"
#: fdmextruder.def.json
msgctxt "machine_settings label"
@ -30,155 +31,152 @@ msgstr "Definições específicas da máquina"
#: fdmextruder.def.json
msgctxt "extruder_nr label"
msgid "Extruder"
msgstr "Extrusora"
msgstr "Extrusor"
#: fdmextruder.def.json
msgctxt "extruder_nr description"
msgid "The extruder train used for printing. This is used in multi-extrusion."
msgstr "A máquina extrusora utilizada para imprimir. Esta é utilizada em extrusões múltiplas."
msgstr "O núcleos de extrusão utilizado para imprimir. Definição usada com múltiplos extrusores."
#: fdmextruder.def.json
msgctxt "machine_nozzle_id label"
msgid "Nozzle ID"
msgstr "ID do bocal"
msgstr "ID do Nozzle"
#: fdmextruder.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 "A ID do bocal para uma máquina de extrusão, tal como \"AA 0.4\" e \"BB 0.8\"."
msgstr "O ID do nozzle de um núcleo de extrusão, tal como \"AA 0.4\" e \"BB 0.8\"."
#: fdmextruder.def.json
msgctxt "machine_nozzle_size label"
msgid "Nozzle Diameter"
msgstr "Diâmetro do bocal"
msgstr "Diâmetro do Nozzle"
#: fdmextruder.def.json
msgctxt "machine_nozzle_size description"
msgid ""
"The inner diameter of the nozzle. Change this setting when using a non-"
"standard nozzle size."
msgstr "O diâmetro interno do bocal. Altere esta definição ao utilizar um tamanho de bocal não convencional."
msgid "The inner diameter of the nozzle. Change this setting when using a non-standard nozzle size."
msgstr ""
"O diâmetro interno do nozzle. Altere esta definição quando utilizar um nozzle com um tamanho não convencional."
#: fdmextruder.def.json
msgctxt "machine_nozzle_offset_x label"
msgid "Nozzle X Offset"
msgstr "Desvio X do bocal"
msgstr "Desvio X do Nozzle"
#: fdmextruder.def.json
msgctxt "machine_nozzle_offset_x description"
msgid "The x-coordinate of the offset of the nozzle."
msgstr "A coordenada X do desvio do bocal."
msgstr "A coordenada X do desvio do nozzle."
#: fdmextruder.def.json
msgctxt "machine_nozzle_offset_y label"
msgid "Nozzle Y Offset"
msgstr "Desvio Y do bocal"
msgstr "Desvio Y do Nozzle"
#: fdmextruder.def.json
msgctxt "machine_nozzle_offset_y description"
msgid "The y-coordinate of the offset of the nozzle."
msgstr "A coordenada Y do desvio do bocal."
msgstr "A coordenada Y do desvio do nozzle."
#: fdmextruder.def.json
msgctxt "machine_extruder_start_code label"
msgid "Extruder Start G-Code"
msgstr "G-Code inicial da extrusora"
msgstr "G-Code Inicial do Extrusor"
#: fdmextruder.def.json
msgctxt "machine_extruder_start_code description"
msgid "Start g-code to execute whenever turning the extruder on."
msgstr "G-Code inicial a ser executado sempre que a extrusora for ligada."
msgstr "G-Code inicial a ser executado sempre que o extrusor for ligado."
#: fdmextruder.def.json
msgctxt "machine_extruder_start_pos_abs label"
msgid "Extruder Start Position Absolute"
msgstr "Posição inicial absoluta da extrusora"
msgstr "Posição Inicial Absoluta do Extrusor"
#: fdmextruder.def.json
msgctxt "machine_extruder_start_pos_abs description"
msgid ""
"Make the extruder starting position absolute rather than relative to the "
"last-known location of the head."
msgstr "Torne a posição inicial da extrusora absoluta em vez de relativa à última posição conhecida da cabeça."
msgid "Make the extruder starting position absolute rather than relative to the last-known location of the head."
msgstr ""
"Define a posição inicial do extrusor, absoluta em vez de relativa à última posição conhecida da cabeça de "
"impressão."
#: fdmextruder.def.json
msgctxt "machine_extruder_start_pos_x label"
msgid "Extruder Start Position X"
msgstr "X da posição inicial da extrusora"
msgstr "Posição X Inicial do Extrusor"
#: fdmextruder.def.json
msgctxt "machine_extruder_start_pos_x description"
msgid "The x-coordinate of the starting position when turning the extruder on."
msgstr "A coordenada X da posição inicial ao ligar a extrusora."
msgstr "A coordenada X da posição inicial ao ligar o extrusor."
#: fdmextruder.def.json
msgctxt "machine_extruder_start_pos_y label"
msgid "Extruder Start Position Y"
msgstr "Y da posição inicial da extrusora"
msgstr "Posição Y Inicial do Extrusor"
#: fdmextruder.def.json
msgctxt "machine_extruder_start_pos_y description"
msgid "The y-coordinate of the starting position when turning the extruder on."
msgstr "A coordenada Y da posição inicial ao ligar a extrusora."
msgstr "A coordenada Y da posição inicial ao ligar o extrusor."
#: fdmextruder.def.json
msgctxt "machine_extruder_end_code label"
msgid "Extruder End G-Code"
msgstr "G-Code final da extrusora"
msgstr "G-Code Final do Extrusor"
#: fdmextruder.def.json
msgctxt "machine_extruder_end_code description"
msgid "End g-code to execute whenever turning the extruder off."
msgstr "G-Code final a ser executado sempre que a extrusora for desligada."
msgstr "G-Code final a ser executado sempre que o extrusor for desligado."
#: fdmextruder.def.json
msgctxt "machine_extruder_end_pos_abs label"
msgid "Extruder End Position Absolute"
msgstr "Posição final absoluta da extrusora"
msgstr "Posição Final Absoluta do Extrusor"
#: fdmextruder.def.json
msgctxt "machine_extruder_end_pos_abs description"
msgid ""
"Make the extruder ending position absolute rather than relative to the last-"
"known location of the head."
msgstr "Torne a posição final da extrusora absoluta em vez de relativa à última localização conhecida da cabeça."
msgid "Make the extruder ending position absolute rather than relative to the last-known location of the head."
msgstr ""
"Define a posição final do extrusor, absoluta em vez de relativa à última posição conhecida da cabeça de "
"impressão."
#: fdmextruder.def.json
msgctxt "machine_extruder_end_pos_x label"
msgid "Extruder End Position X"
msgstr "X da posição final da extrusora"
msgstr "Posição X Final do Extrusor"
#: fdmextruder.def.json
msgctxt "machine_extruder_end_pos_x description"
msgid "The x-coordinate of the ending position when turning the extruder off."
msgstr "A coordenada X da posição final ao desligar a extrusora."
msgstr "A coordenada X da posição final ao desligar o extrusor."
#: fdmextruder.def.json
msgctxt "machine_extruder_end_pos_y label"
msgid "Extruder End Position Y"
msgstr "Y da posição final da extrusora"
msgstr "Posição Y Final do Extrusor"
#: fdmextruder.def.json
msgctxt "machine_extruder_end_pos_y description"
msgid "The y-coordinate of the ending position when turning the extruder off."
msgstr "A coordenada Y da posição final ao desligar a extrusora."
msgstr "A coordenada Y da posição final ao desligar o extrusor."
#: fdmextruder.def.json
msgctxt "extruder_prime_pos_z label"
msgid "Extruder Prime Z Position"
msgstr "Posição Z de preparação da extrusora"
msgstr "Posição Z Preparação do Extrusor"
#: fdmextruder.def.json
msgctxt "extruder_prime_pos_z description"
msgid ""
"The Z coordinate of the position where the nozzle primes at the start of "
"printing."
msgstr "A coordenada Z da posição de preparação do bocal ao iniciar a impressão."
msgid "The Z coordinate of the position where the nozzle primes at the start of printing."
msgstr "A coordenada Z da posição onde o nozzle é preparado ao iniciar a impressão."
#: fdmextruder.def.json
msgctxt "platform_adhesion label"
msgid "Build Plate Adhesion"
msgstr "Aderência à placa de construção"
msgstr "Aderência Base Construção"
#: fdmextruder.def.json
msgctxt "platform_adhesion description"
@ -188,23 +186,19 @@ msgstr "Aderência"
#: fdmextruder.def.json
msgctxt "extruder_prime_pos_x label"
msgid "Extruder Prime X Position"
msgstr "Posição X de preparação da extrusora"
msgstr "Posição X Preparação do Extrusor"
#: fdmextruder.def.json
msgctxt "extruder_prime_pos_x description"
msgid ""
"The X coordinate of the position where the nozzle primes at the start of "
"printing."
msgstr "A coordenada X da posição de preparação do bocal ao iniciar a impressão."
msgid "The X coordinate of the position where the nozzle primes at the start of printing."
msgstr "A coordenada X da posição onde o nozzle é preparado ao iniciar a impressão."
#: fdmextruder.def.json
msgctxt "extruder_prime_pos_y label"
msgid "Extruder Prime Y Position"
msgstr "Posição Y de preparação da extrusora"
msgstr "Posição Y Preparação do Extrusor"
#: fdmextruder.def.json
msgctxt "extruder_prime_pos_y description"
msgid ""
"The Y coordinate of the position where the nozzle primes at the start of "
"printing."
msgstr "A coordenada Y da posição de preparação do bocal ao iniciar a impressão."
msgid "The Y coordinate of the position where the nozzle primes at the start of printing."
msgstr "A coordenada Y da posição onde o nozzle é preparado ao iniciar a impressão."

File diff suppressed because it is too large Load Diff

View File

@ -26,33 +26,54 @@ UM.Dialog
minimumHeight: maximumHeight
minimumWidth: maximumWidth
modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal;
modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal
property var fileUrl
function loadProjectFile(projectFile)
{
UM.WorkspaceFileHandler.readLocalFile(projectFile);
// load the entire project
function loadProjectFile() {
var meshName = backgroundItem.getMeshName(projectFile.toString());
backgroundItem.hasMesh(decodeURIComponent(meshName));
}
function loadModelFiles(fileUrls)
{
for (var i in fileUrls)
{
CuraApplication.readLocalFile(fileUrls[i]);
// update preference
if (rememberChoiceCheckBox.checked) {
UM.Preferences.setValue("cura/choice_on_open_project", "open_as_project")
}
var meshName = backgroundItem.getMeshName(fileUrls[0].toString());
backgroundItem.hasMesh(decodeURIComponent(meshName));
UM.WorkspaceFileHandler.readLocalFile(base.fileUrl)
var meshName = backgroundItem.getMeshName(base.fileUrl.toString())
backgroundItem.hasMesh(decodeURIComponent(meshName))
base.hide()
}
onVisibleChanged:
{
if (visible)
{
// load the project file as separated models
function loadModelFiles() {
// update preference
if (rememberChoiceCheckBox.checked) {
UM.Preferences.setValue("cura/choice_on_open_project", "open_as_model")
}
CuraApplication.readLocalFile(base.fileUrl)
var meshName = backgroundItem.getMeshName(base.fileUrl.toString())
backgroundItem.hasMesh(decodeURIComponent(meshName))
base.hide()
}
// override UM.Dialog accept
function accept () {
var openAsPreference = UM.Preferences.getValue("cura/choice_on_open_project")
// when hitting 'enter', we always open as project unless open_as_model was explicitly stored as preference
if (openAsPreference == "open_as_model") {
loadModelFiles()
} else {
loadProjectFile()
}
}
onVisibleChanged: {
if (visible) {
var rememberMyChoice = UM.Preferences.getValue("cura/choice_on_open_project") != "always_ask";
rememberChoiceCheckBox.checked = rememberMyChoice;
}
@ -90,47 +111,26 @@ UM.Dialog
}
// Buttons
Item
{
Item {
id: buttonBar
anchors.right: parent.right
anchors.left: parent.left
height: childrenRect.height
Button
{
Button {
id: openAsProjectButton
text: catalog.i18nc("@action:button", "Open as project");
text: catalog.i18nc("@action:button", "Open as project")
anchors.right: importModelsButton.left
anchors.rightMargin: UM.Theme.getSize("default_margin").width
isDefault: true
onClicked:
{
// update preference
if (rememberChoiceCheckBox.checked)
UM.Preferences.setValue("cura/choice_on_open_project", "open_as_project");
// load this file as project
base.hide();
loadProjectFile(base.fileUrl);
}
onClicked: loadProjectFile()
}
Button
{
Button {
id: importModelsButton
text: catalog.i18nc("@action:button", "Import models");
text: catalog.i18nc("@action:button", "Import models")
anchors.right: parent.right
onClicked:
{
// update preference
if (rememberChoiceCheckBox.checked)
UM.Preferences.setValue("cura/choice_on_open_project", "open_as_model");
// load models from this project file
base.hide();
loadModelFiles([base.fileUrl]);
}
onClicked: loadModelFiles()
}
}
}

View File

@ -31,7 +31,7 @@ Rectangle
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
property bool collapsed: false;
property bool collapsed: true;
SystemPalette { id: palette }

View File

@ -213,8 +213,8 @@ UM.ManagementPage
ProfileTab
{
title: catalog.i18nc("@title:tab", "Global Settings");
quality: Cura.MachineManager.activeMachine.qualityChanges.id
material: Cura.MachineManager.activeMachine.material.id
quality: base.currentItem != null ? base.currentItem.id : "";
material: Cura.MachineManager.allActiveMaterialIds[Cura.MachineManager.activeMachineId]
}
Repeater

View File

@ -405,7 +405,7 @@ Column
Label {
id: materialInfoLabel
wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "<a href='%1'>Check compatibility</a>")
text: "<a href='%1'>" + catalog.i18nc("@label", "Check compatibility") + "</a>"
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link")

View File

@ -11,8 +11,8 @@ setting_version = 4
[values]
acceleration_enabled = True
acceleration_print = 2000
acceleration_travel = 3500
acceleration_print = 1800
acceleration_travel = 3000
adhesion_type = skirt
brim_width = 4.0
cool_fan_full_at_height = 0.5
@ -23,16 +23,16 @@ infill_pattern = zigzag
infill_sparse_density = 25
initial_layer_line_width_factor = 140
jerk_enabled = True
jerk_print = 13
jerk_travel = 13
layer_height = 0.4
layer_height_0 = 0.4
jerk_print = 8
jerk_travel = 10
layer_height = 0.3
layer_height_0 = 0.3
material_bed_temperature = 60
material_diameter = 1.75
material_print_temperature = 200
material_print_temperature_layer_0 = 0
retract_at_layer_change = False
retraction_amount = 7
retraction_amount = 6
retraction_hop = 0.075
retraction_hop_enabled = True
retraction_hop_only_when_collides = True

View File

@ -11,8 +11,8 @@ setting_version = 4
[values]
acceleration_enabled = True
acceleration_print = 2000
acceleration_travel = 3500
acceleration_print = 1800
acceleration_travel = 3000
adhesion_type = skirt
brim_width = 4.0
cool_fan_full_at_height = 0.5
@ -23,8 +23,8 @@ infill_pattern = zigzag
infill_sparse_density = 25
initial_layer_line_width_factor = 140
jerk_enabled = True
jerk_print = 13
jerk_travel = 13
jerk_print = 8
jerk_travel = 10
layer_height = 0.1
layer_height_0 = 0.1
material_bed_temperature = 60
@ -32,7 +32,7 @@ material_diameter = 1.75
material_print_temperature = 200
material_print_temperature_layer_0 = 0
retract_at_layer_change = False
retraction_amount = 7
retraction_amount = 6
retraction_hop = 0.075
retraction_hop_enabled = True
retraction_hop_only_when_collides = True

View File

@ -11,8 +11,8 @@ setting_version = 4
[values]
acceleration_enabled = True
acceleration_print = 2000
acceleration_travel = 3500
acceleration_print = 1800
acceleration_travel = 3000
adhesion_type = skirt
brim_width = 4.0
cool_fan_full_at_height = 0.5
@ -23,8 +23,8 @@ infill_pattern = zigzag
infill_sparse_density = 25
initial_layer_line_width_factor = 140
jerk_enabled = True
jerk_print = 13
jerk_travel = 13
jerk_print = 8
jerk_travel = 10
layer_height = 0.2
layer_height_0 = 0.2
material_bed_temperature = 60
@ -32,7 +32,7 @@ material_diameter = 1.75
material_print_temperature = 200
material_print_temperature_layer_0 = 0
retract_at_layer_change = False
retraction_amount = 7
retraction_amount = 6
retraction_hop = 0.075
retraction_hop_enabled = True
retraction_hop_only_when_collides = True

View File

@ -0,0 +1,72 @@
[general]
version = 2
name = gMax 1.5+ Dual Normal Layers
definition = gmax15plus_dual
[metadata]
type = quality
quality_type = normal
weight = -1
setting_version = 4
global_quality = True
[values]
layer_height = 0.2
layer_height_0 = 0.28
infill_before_walls = False
infill_overlap = 20
infill_pattern = grid
speed_slowdown_layers = 1
acceleration_enabled = True
acceleration_print = 400
acceleration_travel = 700
coasting_enable = False
coasting_volume = 0.0625
retraction_combing = off
retraction_amount = 0.75
retraction_speed = 70
material_flow = 98
material_diameter = 1.75
support_use_towers = False
support_z_distance = 0.2
support_xy_distance = 1.5
support_join_distance = 10
support_interface_enable = True
support_interface_pattern = lines
adhesion_type = skirt
skirt_gap = 2
cool_fan_speed = 70
cool_fan_speed_max = 85
skin_overlap = 10
speed_print = 50
speed_infill = =math.ceil(speed_print * 0.85)
speed_layer_0 = =math.ceil(speed_print * 0.45)
speed_topbottom = =math.ceil(speed_print * 0.75)
speed_wall = =math.ceil(speed_print * 0.5)
speed_wall_x = =math.ceil(speed_print * 0.7)
top_bottom_pattern = lines
top_layers = 4
top_thickness = 1
bottom_layers = 2
wall_line_count = 2
z_seam_corner = z_seam_corner_none
ooze_shield_angle = 20
ooze_shield_dist = 4
ooze_shield_enabled = True
prime_tower_enable = False
prime_tower_position_x = 350
prime_tower_position_y = 350
prime_tower_wall_thickness = 2
switch_extruder_retraction_amount = 6
switch_extruder_retraction_speeds = 60
material_standby_temperature = =material_print_temperature - 10

View File

@ -0,0 +1,72 @@
[general]
version = 2
name = gMax 1.5+ Dual Thick Layers
definition = gmax15plus_dual
[metadata]
type = quality
quality_type = course
weight = -2
setting_version = 4
global_quality = True
[values]
layer_height = 0.28
layer_height_0 = 0.32
infill_before_walls = False
infill_overlap = 20
infill_pattern = grid
speed_slowdown_layers = 1
acceleration_enabled = True
acceleration_print = 400
acceleration_travel = 700
coasting_enable = False
coasting_volume = 0.0625
retraction_combing = off
retraction_amount = 0.75
retraction_speed = 70
material_flow = 98
material_diameter = 1.75
support_use_towers = False
support_z_distance = 0.2
support_xy_distance = 1.5
support_join_distance = 10
support_interface_enable = True
support_interface_pattern = lines
adhesion_type = skirt
skirt_gap = 2
cool_fan_speed = 70
cool_fan_speed_max = 85
skin_overlap = 10
speed_print = 40
speed_infill = =math.ceil(speed_print * 0.85)
speed_layer_0 = =math.ceil(speed_print * 0.5)
speed_topbottom = =math.ceil(speed_print * 0.75)
speed_wall = =math.ceil(speed_print * 0.5)
speed_wall_x = =math.ceil(speed_print * 0.7)
top_bottom_pattern = lines
top_layers = 3
top_thickness = 1
bottom_layers = 2
wall_line_count = 2
z_seam_corner = z_seam_corner_none
ooze_shield_angle = 20
ooze_shield_dist = 4
ooze_shield_enabled = True
prime_tower_enable = False
prime_tower_position_x = 350
prime_tower_position_y = 350
prime_tower_wall_thickness = 2
switch_extruder_retraction_amount = 6
switch_extruder_retraction_speeds = 60
material_standby_temperature = =material_print_temperature - 10

View File

@ -0,0 +1,72 @@
[general]
version = 2
name = gMax 1.5+ Dual Thin Layers
definition = gmax15plus_dual
[metadata]
type = quality
quality_type = high
weight = 0
setting_version = 4
global_quality = True
[values]
layer_height = 0.16
layer_height_0 = 0.2
infill_before_walls = False
infill_overlap = 20
infill_pattern = grid
speed_slowdown_layers = 1
acceleration_enabled = True
acceleration_print = 400
acceleration_travel = 700
coasting_enable = False
coasting_volume = 0.0625
retraction_combing = off
retraction_amount = 0.75
retraction_speed = 70
material_flow = 98
material_diameter = 1.75
support_use_towers = False
support_z_distance = 0.2
support_xy_distance = 1.5
support_join_distance = 10
support_interface_enable = True
support_interface_pattern = lines
adhesion_type = skirt
skirt_gap = 2
cool_fan_speed = 70
cool_fan_speed_max = 85
skin_overlap = 10
speed_print = 40
speed_infill = =math.ceil(speed_print * 0.85)
speed_layer_0 = =math.ceil(speed_print * 0.4)
speed_topbottom = =math.ceil(speed_print * 0.75)
speed_wall = =math.ceil(speed_print * 0.5)
speed_wall_x = =math.ceil(speed_print * 0.7)
top_bottom_pattern = lines
top_layers = 4
top_thickness = 1
bottom_layers = 2
wall_line_count = 2
z_seam_corner = z_seam_corner_none
ooze_shield_angle = 20
ooze_shield_dist = 4
ooze_shield_enabled = True
prime_tower_enable = False
prime_tower_position_x = 350
prime_tower_position_y = 350
prime_tower_wall_thickness = 2
switch_extruder_retraction_amount = 6
switch_extruder_retraction_speeds = 60
material_standby_temperature = =material_print_temperature - 10

View File

@ -0,0 +1,71 @@
[general]
version = 2
name = gMax 1.5+ Dual Very Thick Layers
definition = gmax15plus_dual
[metadata]
type = quality
quality_type = extra_course
weight = -3
setting_version = 4
global_quality = True
[values]
layer_height = 0.32
infill_before_walls = False
infill_overlap = 20
infill_pattern = grid
speed_slowdown_layers = 1
acceleration_enabled = True
acceleration_print = 400
acceleration_travel = 700
coasting_enable = False
coasting_volume = 0.0625
retraction_combing = off
retraction_amount = 0.75
retraction_speed = 70
material_flow = 98
material_diameter = 1.75
support_use_towers = False
support_z_distance = 0.2
support_xy_distance = 1.5
support_join_distance = 10
support_interface_enable = True
support_interface_pattern = lines
adhesion_type = skirt
skirt_gap = 2
cool_fan_speed = 70
cool_fan_speed_max = 85
skin_overlap = 10
speed_print = 40
speed_infill = =math.ceil(speed_print * 0.85)
speed_layer_0 = =math.ceil(speed_print * 0.45)
speed_topbottom = =math.ceil(speed_print * 0.75)
speed_wall = =math.ceil(speed_print * 0.5)
speed_wall_x = =math.ceil(speed_print * 0.7)
top_bottom_pattern = lines
top_layers = 3
top_thickness = 1
bottom_layers = 2
wall_line_count = 2
z_seam_corner = z_seam_corner_none
ooze_shield_angle = 20
ooze_shield_dist = 4
ooze_shield_enabled = True
prime_tower_enable = False
prime_tower_position_x = 350
prime_tower_position_y = 350
prime_tower_wall_thickness = 2
switch_extruder_retraction_amount = 6
switch_extruder_retraction_speeds = 60
material_standby_temperature = =material_print_temperature - 10

View File

@ -0,0 +1,62 @@
[general]
version = 2
name = gMax 1.5+ Normal Layers
definition = gmax15plus
[metadata]
type = quality
quality_type = normal
weight = -1
setting_version = 4
global_quality = True
[values]
layer_height = 0.2
layer_height_0 = 0.28
infill_before_walls = False
infill_overlap = 20
infill_pattern = grid
speed_slowdown_layers = 1
acceleration_enabled = True
acceleration_print = 400
acceleration_travel = 700
coasting_enable = False
coasting_volume = 0.0625
retraction_combing = off
retraction_amount = 0.75
retraction_speed = 70
material_flow = 98
material_diameter = 1.75
support_use_towers = False
support_z_distance = 0.2
support_xy_distance = 1.5
support_join_distance = 10
support_interface_enable = True
support_interface_pattern = lines
adhesion_type = skirt
skirt_gap = 2
cool_fan_speed = 70
cool_fan_speed_max = 85
skin_overlap = 10
speed_print = 50
speed_infill = =math.ceil(speed_print * 0.85)
speed_layer_0 = =math.ceil(speed_print * 0.6)
speed_topbottom = =math.ceil(speed_print * 0.75)
speed_wall = =math.ceil(speed_print * 0.5)
speed_wall_x = =math.ceil(speed_wall * 0.7)
top_bottom_pattern = lines
top_layers = 4
top_thickness = 1
bottom_layers = 2
wall_line_count = 2
z_seam_corner = z_seam_corner_none

View File

@ -0,0 +1,60 @@
[general]
version = 2
name = gMax 1.5+ Thick Layers
definition = gmax15plus
[metadata]
type = quality
quality_type = course
weight = -2
setting_version = 4
global_quality = True
[values]
layer_height = 0.28
layer_height_0 = 0.32
infill_before_walls = False
infill_overlap = 20
infill_pattern = grid
speed_slowdown_layers = 1
acceleration_enabled = True
acceleration_print = 400
acceleration_travel = 700
coasting_enable = False
coasting_volume = 0.0625
retraction_combing = off
retraction_amount = 0.75
retraction_speed = 70
material_flow = 98
material_diameter = 1.75
support_use_towers = False
support_z_distance = 0.2
support_xy_distance = 1.5
support_join_distance = 10
support_interface_enable = True
support_interface_pattern = lines
adhesion_type = skirt
skirt_gap = 2
cool_fan_speed = 70
cool_fan_speed_max = 85
skin_overlap = 10
speed_print = 50
speed_infill = =math.ceil(speed_print * 0.85)
speed_layer_0 = =math.ceil(speed_print * 0.6)
speed_topbottom = =math.ceil(speed_print * 0.75)
speed_wall = =math.ceil(speed_print * 0.5)
speed_wall_x = =math.ceil(speed_wall * 0.7)
top_bottom_pattern = lines
top_layers = 3
top_thickness = 1
bottom_layers = 2
wall_line_count = 2
z_seam_corner = z_seam_corner_none

View File

@ -0,0 +1,60 @@
[general]
version = 2
name = gMax 1.5+ Thin Layers
definition = gmax15plus
[metadata]
type = quality
quality_type = high
weight = 0
setting_version = 4
global_quality = True
[values]
layer_height = 0.16
layer_height_0 = 0.2
infill_before_walls = False
infill_overlap = 20
infill_pattern = grid
speed_slowdown_layers = 1
acceleration_enabled = True
acceleration_print = 400
acceleration_travel = 700
coasting_enable = False
coasting_volume = 0.0625
retraction_combing = off
retraction_amount = 0.75
retraction_speed = 70
material_flow = 98
material_diameter = 1.75
support_use_towers = False
support_z_distance = 0.2
support_xy_distance = 1.5
support_join_distance = 10
support_interface_enable = True
support_interface_pattern = lines
adhesion_type = skirt
skirt_gap = 2
cool_fan_speed = 70
cool_fan_speed_max = 85
skin_overlap = 10
speed_print = 55
speed_infill = =math.ceil(speed_print * 0.85)
speed_layer_0 = =math.ceil(speed_print * 0.6)
speed_topbottom = =math.ceil(speed_print * 0.75)
speed_wall = =math.ceil(speed_print * 0.5)
speed_wall_x = =math.ceil(speed_wall * 0.7)
top_bottom_pattern = lines
top_layers = 5
top_thickness = 1
bottom_layers = 3
wall_line_count = 2
z_seam_corner = z_seam_corner_none

View File

@ -0,0 +1,59 @@
[general]
version = 2
name = gMax 1.5+ Very Thick Layers
definition = gmax15plus
[metadata]
type = quality
quality_type = extra_course
weight = -3
setting_version = 4
global_quality = True
[values]
layer_height = 0.32
infill_before_walls = False
infill_overlap = 20
infill_pattern = grid
speed_slowdown_layers = 1
acceleration_enabled = True
acceleration_print = 400
acceleration_travel = 700
coasting_enable = False
coasting_volume = 0.0625
retraction_combing = off
retraction_amount = 0.75
retraction_speed = 70
material_flow = 98
material_diameter = 1.75
support_use_towers = False
support_z_distance = 0.2
support_xy_distance = 1.5
support_join_distance = 10
support_interface_enable = True
support_interface_pattern = lines
adhesion_type = skirt
skirt_gap = 2
cool_fan_speed = 70
cool_fan_speed_max = 85
skin_overlap = 10
speed_print = 45
speed_infill = =math.ceil(speed_print * 0.85)
speed_layer_0 = =math.ceil(speed_print * 0.6)
speed_topbottom = =math.ceil(speed_print * 0.75)
speed_wall = =math.ceil(speed_print * 0.6)
speed_wall_x = =math.ceil(speed_wall * 0.7)
top_bottom_pattern = lines
top_layers = 3
top_thickness = 1
bottom_layers = 2
wall_line_count = 2
z_seam_corner = z_seam_corner_none

View File

@ -12,7 +12,7 @@ setting_version = 4
[values]
brim_width = 4.0
infill_pattern = zigzag
layer_height = 0.4
layer_height = 0.3
material_diameter = 1.75
speed_infill = 50
speed_print = 50

View File

@ -7,6 +7,7 @@ definition = cartesio
author = Cartesio
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.25

View File

@ -7,6 +7,7 @@ definition = cartesio
author = Cartesio
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.4

View File

@ -7,6 +7,7 @@ definition = cartesio
author = Cartesio
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.8

View File

@ -7,6 +7,7 @@ definition = fabtotum
author = FABtotum
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.35

View File

@ -7,6 +7,7 @@ definition = fabtotum
author = FABtotum
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.4

View File

@ -7,6 +7,7 @@ definition = fabtotum
author = FABtotum
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.6

View File

@ -7,6 +7,7 @@ definition = fabtotum
author = FABtotum
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.2

View File

@ -7,6 +7,7 @@ definition = fabtotum
author = FABtotum
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.4

View File

@ -7,6 +7,7 @@ definition = fabtotum
author = FABtotum
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.6

View File

@ -7,6 +7,7 @@ definition = fabtotum
author = FABtotum
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.8

View File

@ -0,0 +1,12 @@
[general]
name = 0.25mm E3D (Difficult)
version = 2
definition = gmax15plus
[metadata]
author = gcreate
type = variant
setting_version = 4
[values]
machine_nozzle_size = 0.25

View File

@ -0,0 +1,12 @@
[general]
name = 0.4mm E3D
version = 2
definition = gmax15plus
[metadata]
author = gcreate
type = variant
setting_version = 4
[values]
machine_nozzle_size = 0.4

View File

@ -0,0 +1,12 @@
[general]
name = 0.5mm E3D (Default)
version = 2
definition = gmax15plus
[metadata]
author = gcreate
type = variant
setting_version = 4
[values]
machine_nozzle_size = 0.5

View File

@ -0,0 +1,12 @@
[general]
name = 0.5mm J-Head
version = 2
definition = gmax15plus
[metadata]
author = gcreate
type = variant
setting_version = 4
[values]
machine_nozzle_size = 0.5

View File

@ -0,0 +1,12 @@
[general]
name = 0.6mm E3D
version = 2
definition = gmax15plus
[metadata]
author = gcreate
type = variant
setting_version = 4
[values]
machine_nozzle_size = 0.6

View File

@ -0,0 +1,12 @@
[general]
name = 0.8mm E3D
version = 2
definition = gmax15plus
[metadata]
author = gcreate
type = variant
setting_version = 4
[values]
machine_nozzle_size = 0.8

View File

@ -0,0 +1,12 @@
[general]
name = 1.0mm J-Head
version = 2
definition = gmax15plus
[metadata]
author = gcreate
type = variant
setting_version = 4
[values]
machine_nozzle_size = 0.5

View File

@ -0,0 +1,12 @@
[general]
name = 0.25mm E3D (Difficult)
version = 2
definition = gmax15plus_dual
[metadata]
author = gcreate
type = variant
setting_version = 4
[values]
machine_nozzle_size = 0.25

View File

@ -0,0 +1,12 @@
[general]
name = 0.4mm E3D
version = 2
definition = gmax15plus_dual
[metadata]
author = gcreate
type = variant
setting_version = 4
[values]
machine_nozzle_size = 0.4

View File

@ -0,0 +1,12 @@
[general]
name = 0.5mm E3D (Default)
version = 2
definition = gmax15plus_dual
[metadata]
author = gcreate
type = variant
setting_version = 4
[values]
machine_nozzle_size = 0.5

View File

@ -0,0 +1,12 @@
[general]
name = 0.5mm J-Head
version = 2
definition = gmax15plus_dual
[metadata]
author = gcreate
type = variant
setting_version = 4
[values]
machine_nozzle_size = 0.5

View File

@ -0,0 +1,12 @@
[general]
name = 0.6mm E3D
version = 2
definition = gmax15plus_dual
[metadata]
author = gcreate
type = variant
setting_version = 4
[values]
machine_nozzle_size = 0.6

View File

@ -0,0 +1,12 @@
[general]
name = 0.8mm E3D
version = 2
definition = gmax15plus_dual
[metadata]
author = gcreate
type = variant
setting_version = 4
[values]
machine_nozzle_size = 0.8

View File

@ -0,0 +1,12 @@
[general]
name = 1.0mm J-Head
version = 2
definition = gmax15plus_dual
[metadata]
author = gcreate
type = variant
setting_version = 4
[values]
machine_nozzle_size = 0.5

View File

@ -7,6 +7,7 @@ definition = imade3d_jellybox
author = IMADE3D
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.4

View File

@ -7,6 +7,7 @@ definition = imade3d_jellybox
author = IMADE3D
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.4

View File

@ -7,6 +7,7 @@ definition = ultimaker2
author = Ultimaker
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.25

View File

@ -7,6 +7,7 @@ definition = ultimaker2
author = Ultimaker
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.4

View File

@ -7,6 +7,7 @@ definition = ultimaker2
author = Ultimaker
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.6

View File

@ -7,6 +7,7 @@ definition = ultimaker2
author = Ultimaker
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.8

View File

@ -7,6 +7,7 @@ definition = ultimaker2_extended
author = Ultimaker
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.25

View File

@ -7,6 +7,7 @@ definition = ultimaker2_extended
author = Ultimaker
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.4

View File

@ -7,6 +7,7 @@ definition = ultimaker2_extended
author = Ultimaker
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.6

View File

@ -7,6 +7,7 @@ definition = ultimaker2_extended
author = Ultimaker
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.8

View File

@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus
author = Ultimaker
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.25

View File

@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus
author = Ultimaker
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.4

View File

@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus
author = Ultimaker
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.6

View File

@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus
author = Ultimaker
type = variant
setting_version = 4
hardware_type = nozzle
[values]
machine_nozzle_size = 0.8

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