diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 02d5ff1c67..ee084a764f 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -74,10 +74,14 @@ class BuildVolume(SceneNode): self._adhesion_type = None self._platform = Platform(self) - self._active_container_stack = None + self._global_container_stack = None Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged) self._onGlobalContainerStackChanged() + self._active_extruder_stack = None + ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged) + self._onActiveExtruderStackChanged() + def setWidth(self, width): if width: self._width = width @@ -208,22 +212,22 @@ class BuildVolume(SceneNode): "@info:status", "The build volume height has been reduced due to the value of the" " \"Print Sequence\" setting to prevent the gantry from colliding" - " with printed objects."), lifetime=10).show() + " with printed models."), lifetime=10).show() def getRaftThickness(self): return self._raft_thickness def _updateRaftThickness(self): old_raft_thickness = self._raft_thickness - self._adhesion_type = self._active_container_stack.getProperty("adhesion_type", "value") + self._adhesion_type = self._global_container_stack.getProperty("adhesion_type", "value") self._raft_thickness = 0.0 if self._adhesion_type == "raft": self._raft_thickness = ( - self._active_container_stack.getProperty("raft_base_thickness", "value") + - self._active_container_stack.getProperty("raft_interface_thickness", "value") + - self._active_container_stack.getProperty("raft_surface_layers", "value") * - self._active_container_stack.getProperty("raft_surface_thickness", "value") + - self._active_container_stack.getProperty("raft_airgap", "value")) + self._global_container_stack.getProperty("raft_base_thickness", "value") + + self._global_container_stack.getProperty("raft_interface_thickness", "value") + + self._global_container_stack.getProperty("raft_surface_layers", "value") * + self._global_container_stack.getProperty("raft_surface_thickness", "value") + + self._global_container_stack.getProperty("raft_airgap", "value")) # Rounding errors do not matter, we check if raft_thickness has changed at all if old_raft_thickness != self._raft_thickness: @@ -231,41 +235,52 @@ class BuildVolume(SceneNode): self.raftThicknessChanged.emit() def _onGlobalContainerStackChanged(self): - if self._active_container_stack: - self._active_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged) + if self._global_container_stack: + self._global_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged) - self._active_container_stack = Application.getInstance().getGlobalContainerStack() + self._global_container_stack = Application.getInstance().getGlobalContainerStack() - if self._active_container_stack: - self._active_container_stack.propertyChanged.connect(self._onSettingPropertyChanged) + if self._global_container_stack: + self._global_container_stack.propertyChanged.connect(self._onSettingPropertyChanged) - self._width = self._active_container_stack.getProperty("machine_width", "value") - if self._active_container_stack.getProperty("print_sequence", "value") == "one_at_a_time": - self._height = self._active_container_stack.getProperty("gantry_height", "value") - self._buildVolumeMessage() + self._width = self._global_container_stack.getProperty("machine_width", "value") + machine_height = self._global_container_stack.getProperty("machine_height", "value") + if self._global_container_stack.getProperty("print_sequence", "value") == "one_at_a_time": + self._height = min(self._global_container_stack.getProperty("gantry_height", "value"), machine_height) + if self._height < machine_height: + self._buildVolumeMessage() else: - self._height = self._active_container_stack.getProperty("machine_height", "value") - self._depth = self._active_container_stack.getProperty("machine_depth", "value") + self._height = self._global_container_stack.getProperty("machine_height", "value") + self._depth = self._global_container_stack.getProperty("machine_depth", "value") self._updateDisallowedAreas() self._updateRaftThickness() self.rebuild() + def _onActiveExtruderStackChanged(self): + if self._active_extruder_stack: + self._active_extruder_stack.propertyChanged.disconnect(self._onSettingPropertyChanged) + self._active_extruder_stack = ExtruderManager.getInstance().getActiveExtruderStack() + if self._active_extruder_stack: + self._active_extruder_stack.propertyChanged.connect(self._onSettingPropertyChanged) + def _onSettingPropertyChanged(self, setting_key, property_name): if property_name != "value": return rebuild_me = False if setting_key == "print_sequence": + machine_height = self._global_container_stack.getProperty("machine_height", "value") if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time": - self._height = self._active_container_stack.getProperty("gantry_height", "value") - self._buildVolumeMessage() + self._height = min(self._global_container_stack.getProperty("gantry_height", "value"), machine_height) + if self._height < machine_height: + self._buildVolumeMessage() else: - self._height = self._active_container_stack.getProperty("machine_height", "value") + self._height = self._global_container_stack.getProperty("machine_height", "value") rebuild_me = True - if setting_key in self._skirt_settings: + if setting_key in self._skirt_settings or setting_key in self._prime_settings or setting_key in self._tower_settings: self._updateDisallowedAreas() rebuild_me = True @@ -277,19 +292,33 @@ class BuildVolume(SceneNode): self.rebuild() def _updateDisallowedAreas(self): - if not self._active_container_stack: + if not self._global_container_stack: return disallowed_areas = copy.deepcopy( - self._active_container_stack.getProperty("machine_disallowed_areas", "value")) + self._global_container_stack.getProperty("machine_disallowed_areas", "value")) areas = [] + machine_width = self._global_container_stack.getProperty("machine_width", "value") + machine_depth = self._global_container_stack.getProperty("machine_depth", "value") + + # Add prima tower location as disallowed area. + if self._global_container_stack.getProperty("prime_tower_enable", "value"): + half_prime_tower_size = self._global_container_stack.getProperty("prime_tower_size", "value") / 2 + prime_tower_x = self._global_container_stack.getProperty("prime_tower_position_x", "value") - machine_width / 2 + prime_tower_y = - self._global_container_stack.getProperty("prime_tower_position_y", "value") + machine_depth / 2 + + disallowed_areas.append([ + [prime_tower_x - half_prime_tower_size, prime_tower_y - half_prime_tower_size], + [prime_tower_x + half_prime_tower_size, prime_tower_y - half_prime_tower_size], + [prime_tower_x + half_prime_tower_size, prime_tower_y + half_prime_tower_size], + [prime_tower_x - half_prime_tower_size, prime_tower_y + half_prime_tower_size], + ]) + # Add extruder prime locations as disallowed areas. # Probably needs some rework after coordinate system change. extruder_manager = ExtruderManager.getInstance() - extruders = extruder_manager.getMachineExtruders(self._active_container_stack.getId()) - machine_width = self._active_container_stack.getProperty("machine_width", "value") - machine_depth = self._active_container_stack.getProperty("machine_depth", "value") + extruders = extruder_manager.getMachineExtruders(self._global_container_stack.getId()) for single_extruder in extruders: extruder_prime_pos_x = single_extruder.getProperty("extruder_prime_pos_x", "value") extruder_prime_pos_y = single_extruder.getProperty("extruder_prime_pos_y", "value") @@ -305,7 +334,7 @@ class BuildVolume(SceneNode): [prime_x - PRIME_CLEARANCE, prime_y + PRIME_CLEARANCE], ]) - bed_adhesion_size = self._getBedAdhesionSize(self._active_container_stack) + bed_adhesion_size = self._getBedAdhesionSize(self._global_container_stack) if disallowed_areas: # Extend every area already in the disallowed_areas with the skirt size. @@ -317,8 +346,8 @@ class BuildVolume(SceneNode): # Add the skirt areas around the borders of the build plate. if bed_adhesion_size > 0: - half_machine_width = self._active_container_stack.getProperty("machine_width", "value") / 2 - half_machine_depth = self._active_container_stack.getProperty("machine_depth", "value") / 2 + half_machine_width = self._global_container_stack.getProperty("machine_width", "value") / 2 + half_machine_depth = self._global_container_stack.getProperty("machine_depth", "value") / 2 areas.append(Polygon(numpy.array([ [-half_machine_width, -half_machine_depth], @@ -377,3 +406,5 @@ class BuildVolume(SceneNode): _skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "brim_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist", "xy_offset"] _raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap"] + _prime_settings = ["extruder_prime_pos_x", "extruder_prime_pos_y", "extruder_prime_pos_z"] + _tower_settings = ["prime_tower_enable", "prime_tower_size", "prime_tower_position_x", "prime_tower_position_y"] diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 47512872da..04c666f7b9 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -242,11 +242,17 @@ class CuraApplication(QtApplication): raft_airgap layer_0_z_overlap raft_surface_layers + dual + adhesion_extruder_nr + support_extruder_nr + prime_tower_enable + prime_tower_size + prime_tower_position_x + prime_tower_position_y meshfix blackmagic print_sequence infill_mesh - dual experimental """.replace("\n", ";").replace(" ", "")) @@ -475,6 +481,7 @@ class CuraApplication(QtApplication): qmlRegisterType(cura.Settings.ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel") qmlRegisterType(cura.Settings.MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler") + qmlRegisterType(cura.Settings.QualitySettingsModel, "Cura", 1, 0, "QualitySettingsModel") qmlRegisterSingletonType(cura.Settings.ContainerManager, "Cura", 1, 0, "ContainerManager", cura.Settings.ContainerManager.createContainerManager) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 56daaddc18..32d541f8d6 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -41,6 +41,10 @@ class PlatformPhysics: root = self._controller.getScene().getRoot() + # Keep a list of nodes that are moving. We use this so that we don't move two intersecting objects in the + # same direction. + transformed_nodes = [] + for node in BreadthFirstIterator(root): if node is root or type(node) is not SceneNode or node.getBoundingBox() is None: continue @@ -91,6 +95,9 @@ class PlatformPhysics: if not other_node.callDecoration("getConvexHull") or not other_node.getBoundingBox(): continue + if other_node in transformed_nodes: + continue # Other node is already moving, wait for next pass. + # Get the overlap distance for both convex hulls. If this returns None, there is no intersection. head_hull = node.callDecoration("getConvexHullHead") if head_hull: @@ -125,6 +132,7 @@ class PlatformPhysics: node._outside_buildarea = True if not Vector.Null.equals(move_vector, epsilon=1e-5): + transformed_nodes.append(node) op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector) op.push() diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index b24fe11f18..c376bb496c 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -1,10 +1,15 @@ +from UM.i18n import i18nCatalog from UM.OutputDevice.OutputDevice import OutputDevice from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject +from PyQt5.QtWidgets import QMessageBox + from enum import IntEnum # For the connection state tracking. from UM.Logger import Logger - +from UM.Application import Application from UM.Signal import signalemitter +i18n_catalog = i18nCatalog("cura") + ## Printer output device adds extra interface options on top of output device. # # The assumption is made the printer is a FDM printer. @@ -276,6 +281,11 @@ class PrinterOutputDevice(QObject, OutputDevice): self._hotend_ids[index] = hotend_id self.hotendIdChanged.emit(index, hotend_id) + ## Let the user decide if the hotends and/or material should be synced with the printer + # NB: the UX needs to be implemented by the plugin + def materialHotendChangedMessage(self, callback): + Logger.log("w", "materialHotendChangedMessage needs to be implemented, returning 'Yes'") + callback(QMessageBox.Yes) ## Attempt to establish connection def connect(self): @@ -329,7 +339,7 @@ class PrinterOutputDevice(QObject, OutputDevice): return self._head_z ## Update the saved position of the head - # This function should be called when a new position for the head is recieved. + # This function should be called when a new position for the head is received. def _updateHeadPosition(self, x, y ,z): position_changed = False if self._head_x != x: diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index c23823d71f..3dbd179700 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -351,47 +351,49 @@ class ContainerManager(QObject): return { "status": "success", "message": "Successfully imported container {0}".format(container.getName()) } + ## Update the current active quality changes container with the settings from the user container. + # + # This will go through the active global stack and all active extruder stacks and merge the changes from the user + # container into the quality_changes container. After that, the user container is cleared. + # + # \return \type{bool} True if successful, False if not. @pyqtSlot(result = bool) def updateQualityChanges(self): global_stack = UM.Application.getInstance().getGlobalContainerStack() - - containers_to_merge = [] - - global_quality_changes = global_stack.findContainer(type = "quality_changes") - if not global_quality_changes or global_quality_changes.isReadOnly(): - UM.Logger.log("e", "Could not update quality of a nonexistant or read only quality profile") + if not global_stack: return False UM.Application.getInstance().getMachineManager().blurSettings.emit() - containers_to_merge.append((global_quality_changes, global_stack.getTop())) - - for extruder in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()): - quality_changes = extruder.findContainer(type = "quality_changes") + for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): + # Find the quality_changes container for this stack and merge the contents of the top container into it. + quality_changes = stack.findContainer(type = "quality_changes") if not quality_changes or quality_changes.isReadOnly(): - UM.Logger.log("e", "Could not update quality of a nonexistant or read only quality profile") - return False + UM.Logger.log("e", "Could not update quality of a nonexistant or read only quality profile in stack %s", stack.getId()) + continue - containers_to_merge.append((quality_changes, extruder.getTop())) - - for merge_into, merge in containers_to_merge: - self._performMerge(merge_into, merge) + self._performMerge(quality_changes, stack.getTop()) UM.Application.getInstance().getMachineManager().activeQualityChanged.emit() return True + ## Clear the top-most (user) containers of the active stacks. @pyqtSlot() def clearUserContainers(self): - global_stack = UM.Application.getInstance().getGlobalContainerStack() - UM.Application.getInstance().getMachineManager().blurSettings.emit() - for extruder in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()): - extruder.getTop().clear() - - global_stack.getTop().clear() + # Go through global and extruder stacks and clear their topmost container (the user settings). + for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): + stack.getTop().clear() + ## Create quality changes containers from the user containers in the active stacks. + # + # This will go through the global and extruder stacks and create quality_changes containers from + # the user containers in each stack. These then replace the quality_changes containers in the + # stack and clear the user settings. + # + # \return \type{bool} True if the operation was successfully, False if not. @pyqtSlot(result = bool) def createQualityChanges(self): global_stack = UM.Application.getInstance().getGlobalContainerStack() @@ -406,39 +408,115 @@ class ContainerManager(QObject): UM.Application.getInstance().getMachineManager().blurSettings.emit() unique_name = UM.Settings.ContainerRegistry.getInstance().uniqueName(quality_container.getName()) - unique_id = unique_name.lower() - unique_id.replace(" ", "_") - stacks = [ s for s in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()) ] - stacks.insert(0, global_stack) - - for stack in stacks: + # Go through the active stacks and create quality_changes containers from the user containers. + for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): user_container = stack.getTop() quality_container = stack.findContainer(type = "quality") quality_changes_container = stack.findContainer(type = "quality_changes") if not quality_container or not quality_changes_container: UM.Logger.log("w", "No quality or quality changes container found in stack %s, ignoring it", stack.getId()) - return False + continue - new_quality_changes = user_container.duplicate(stack.getId() + "_" + unique_id, unique_name) - new_quality_changes.setMetaDataEntry("type", "quality_changes") - new_quality_changes.addMetaDataEntry("quality", quality_container.getId()) + new_changes = self._createQualityChanges(quality_container, unique_name, stack.getId()) + self._performMerge(new_changes, user_container) - if not global_stack.getMetaDataEntry("has_machine_quality"): - new_quality_changes.setDefinition(UM.Settings.ContainerRegistry.getInstance().findContainers(id = "fdmprinter")[0]) - - if global_stack.getMetaDataEntry("has_materials"): - material = stack.findContainer(type = "material") - new_quality_changes.addMetaDataEntry("material", material.getId()) - - UM.Settings.ContainerRegistry.getInstance().addContainer(new_quality_changes) - - stack.replaceContainer(stack.getContainerIndex(quality_changes_container), new_quality_changes) - stack.getTop().clear() + UM.Settings.ContainerRegistry.getInstance().addContainer(new_changes) + stack.replaceContainer(stack.getContainerIndex(quality_changes_container), new_changes) UM.Application.getInstance().getMachineManager().activeQualityChanged.emit() return True + ## Remove all quality changes containers matching a specified name. + # + # This will search for quality_changes containers matching the supplied name and remove them. + # Note that if the machine specifies that qualities should be filtered by machine and/or material + # only the containers related to the active machine/material are removed. + # + # \param quality_name The name of the quality changes to remove. + # + # \return \type{bool} True if successful, False if not. + @pyqtSlot(str, result = bool) + def removeQualityChanges(self, quality_name): + if not quality_name: + return False + + for container in self._getFilteredContainers(name = quality_name, type = "quality_changes"): + UM.Settings.ContainerRegistry.getInstance().removeContainer(container.getId()) + + return True + + ## Rename a set of quality changes containers. + # + # This will search for quality_changes containers matching the supplied name and rename them. + # Note that if the machine specifies that qualities should be filtered by machine and/or material + # only the containers related to the active machine/material are renamed. + # + # \param quality_name The name of the quality changes containers to rename. + # \param new_name The new name of the quality changes. + # + # \return True if successful, False if not. + @pyqtSlot(str, str, result = bool) + def renameQualityChanges(self, quality_name, new_name): + if not quality_name or not new_name: + return False + + if quality_name == new_name: + return True + + global_stack = UM.Application.getInstance().getGlobalContainerStack() + if not global_stack: + return False + + UM.Application.getInstance().getMachineManager().blurSettings.emit() + + new_name = UM.Settings.ContainerRegistry.getInstance().uniqueName(new_name) + + container_registry = UM.Settings.ContainerRegistry.getInstance() + for container in self._getFilteredContainers(name = quality_name, type = "quality_changes"): + stack_id = container.getMetaDataEntry("extruder", global_stack.getId()) + container_registry.renameContainer(container.getId(), new_name, self._createUniqueId(stack_id, new_name)) + + UM.Application.getInstance().getMachineManager().activeQualityChanged.emit() + return True + + ## Duplicate a specified set of quality or quality_changes containers. + # + # This will search for containers matching the specified name. If the container is a "quality" type container, a new + # quality_changes container will be created with the specified quality as base. If the container is a "quality_changes" + # container, it is simply duplicated and renamed. + # + # \param quality_name The name of the quality to duplicate. + # + # \return A string containing the name of the duplicated containers, or an empty string if it failed. + @pyqtSlot(str, result = str) + def duplicateQualityOrQualityChanges(self, quality_name): + global_stack = UM.Application.getInstance().getGlobalContainerStack() + if not global_stack or not quality_name: + return "" + + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(name = quality_name) + if not containers: + return "" + + new_name = UM.Settings.ContainerRegistry.getInstance().uniqueName(quality_name) + + container_type = containers[0].getMetaDataEntry("type") + if container_type == "quality": + for container in self._getFilteredContainers(name = quality_name, type = "quality"): + for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): + new_changes = self._createQualityChanges(container, new_name, stack.getId()) + UM.Settings.ContainerRegistry.getInstance().addContainer(new_changes) + elif container_type == "quality_changes": + for container in self._getFilteredContainers(name = quality_name, type = "quality_changes"): + stack_id = container.getMetaDataEntry("extruder", global_stack.getId()) + new_container = container.duplicate(self._createUniqueId(stack_id, new_name), new_name) + UM.Settings.ContainerRegistry.getInstance().addContainer(new_container) + else: + return "" + + return new_name + # Factory function, used by QML @staticmethod def createContainerManager(engine, js_engine): @@ -498,3 +576,84 @@ class ContainerManager(QObject): name_filter = "{0} ({1})".format(mime_type.comment, suffix_list) self._container_name_filters[name_filter] = entry + + ## Return a generator that iterates over a set of containers that are filtered by machine and material when needed. + # + # \param kwargs Initial search criteria that the containers need to match. + # + # \return A generator that iterates over the list of containers matching the search criteria. + def _getFilteredContainers(self, **kwargs): + global_stack = UM.Application.getInstance().getGlobalContainerStack() + if not global_stack: + return False + + criteria = kwargs + + filter_by_material = False + + if global_stack.getMetaDataEntry("has_machine_quality"): + criteria["definition"] = global_stack.getBottom().getId() + + filter_by_material = global_stack.getMetaDataEntry("has_materials") + + material_ids = [] + if filter_by_material: + for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): + material_ids.append(stack.findContainer(type = "material").getId()) + + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria) + for container in containers: + # If the machine specifies we should filter by material, exclude containers that do not match any active material. + if filter_by_material and container.getMetaDataEntry("material") not in material_ids: + continue + + yield container + + ## Creates a unique ID for a container by prefixing the name with the stack ID. + # + # This method creates a unique ID for a container by prefixing it with a specified stack ID. + # This is done to ensure we have an easily identified ID for quality changes, which have the + # same name across several stacks. + # + # \param stack_id The ID of the stack to prepend. + # \param container_name The name of the container that we are creating a unique ID for. + # + # \return Container name prefixed with stack ID, in lower case with spaces replaced by underscores. + def _createUniqueId(self, stack_id, container_name): + result = stack_id + "_" + container_name + result = result.lower() + result.replace(" ", "_") + return result + + ## Create a quality changes container for a specified quality container. + # + # \param quality_container The quality container to create a changes container for. + # \param new_name The name of the new quality_changes container. + # \param stack_id The ID of the container stack the new container "belongs to". It is used primarily to ensure a unique ID. + # + # \return A new quality_changes container with the specified container as base. + def _createQualityChanges(self, quality_container, new_name, stack_id): + global_stack = UM.Application.getInstance().getGlobalContainerStack() + assert global_stack is not None + + # Create a new quality_changes container for the quality. + quality_changes = UM.Settings.InstanceContainer(self._createUniqueId(stack_id, new_name)) + quality_changes.setName(new_name) + quality_changes.addMetaDataEntry("type", "quality_changes") + quality_changes.addMetaDataEntry("quality", quality_container.getMetaDataEntry("quality_type")) + + # If we are creating a container for an extruder, ensure we add that to the container + if stack_id != global_stack.getId(): + quality_changes.addMetaDataEntry("extruder", stack_id) + + # If the machine specifies qualities should be filtered, ensure we match the current criteria. + if not global_stack.getMetaDataEntry("has_machine_quality"): + quality_changes.setDefinition(UM.Settings.ContainerRegistry.getInstance().findContainers(id = "fdmprinter")[0]) + else: + quality_changes.setDefinition(global_stack.getBottom()) + + if global_stack.getMetaDataEntry("has_materials"): + material = quality_container.getMetaDataEntry("material") + quality_changes.addMetaDataEntry("material", material) + + return quality_changes diff --git a/cura/Settings/ContainerSettingsModel.py b/cura/Settings/ContainerSettingsModel.py index a0bdb7f41f..f0fb91d471 100644 --- a/cura/Settings/ContainerSettingsModel.py +++ b/cura/Settings/ContainerSettingsModel.py @@ -41,7 +41,6 @@ class ContainerSettingsModel(ListModel): keys = keys + list(container.getAllKeys()) keys = list(set(keys)) # remove duplicate keys - keys.sort() for key in keys: definition = None @@ -72,6 +71,7 @@ class ContainerSettingsModel(ListModel): "unit": definition.unit, "category": category.label }) + self.sort(lambda k: (k["category"], k["key"])) ## Set the ids of the containers which have the settings this model should list. # Also makes sure the model updates when the containers have property changes diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 990143f7bb..ed7cf3c430 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -170,6 +170,7 @@ class CuraContainerRegistry(ContainerRegistry): profile.addMetaDataEntry("material", self._activeMaterialId()) else: profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0]) + ContainerRegistry.getInstance().addContainer(profile) ## Gets a list of profile writer plugins diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 30a0abefa0..c9895be697 100644 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -131,9 +131,9 @@ class ExtruderManager(QObject): # Make sure the next stack is a stack that contains only the machine definition if not extruder_train.getNextStack(): - shallowStack = UM.Settings.ContainerStack(machine_id + "_shallow") - shallowStack.addContainer(machine_definition) - extruder_train.setNextStack(shallowStack) + shallow_stack = UM.Settings.ContainerStack(machine_id + "_shallow") + shallow_stack.addContainer(machine_definition) + extruder_train.setNextStack(shallow_stack) changed = True if changed: self.extrudersChanged.emit(machine_id) @@ -251,9 +251,9 @@ class ExtruderManager(QObject): # Make sure the next stack is a stack that contains only the machine definition if not container_stack.getNextStack(): - shallowStack = UM.Settings.ContainerStack(machine_id + "_shallow") - shallowStack.addContainer(machine_definition) - container_stack.setNextStack(shallowStack) + shallow_stack = UM.Settings.ContainerStack(machine_id + "_shallow") + shallow_stack.addContainer(machine_definition) + container_stack.setNextStack(shallow_stack) container_registry.addContainer(container_stack) @@ -277,6 +277,20 @@ class ExtruderManager(QObject): for name in self._extruder_trains[machine_id]: yield self._extruder_trains[machine_id][name] + ## Returns a generator that will iterate over the global stack and per-extruder stacks. + # + # The first generated element is the global container stack. After that any extruder stacks are generated. + def getActiveGlobalAndExtruderStacks(self): + global_stack = UM.Application.getInstance().getGlobalContainerStack() + if not global_stack: + return + + yield global_stack + + global_id = global_stack.getId() + for name in self._extruder_trains[global_id]: + yield self._extruder_trains[global_id][name] + def __globalContainerStackChanged(self): self._addCurrentMachineExtruders() self.activeExtruderChanged.emit() diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index a7f9764a20..b1d3f97f3e 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -26,6 +26,7 @@ class MachineManager(QObject): self._global_container_stack = None Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged) + ## When the global container is changed, active material probably needs to be updated. self.globalContainerChanged.connect(self.activeMaterialChanged) self.globalContainerChanged.connect(self.activeVariantChanged) self.globalContainerChanged.connect(self.activeQualityChanged) @@ -36,8 +37,6 @@ class MachineManager(QObject): ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged) self._onActiveExtruderStackChanged() - ## When the global container is changed, active material probably needs to be updated. - ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeMaterialChanged) ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeVariantChanged) ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeQualityChanged) @@ -118,16 +117,7 @@ class MachineManager(QObject): if matching_extruder and matching_extruder.findContainer({"type": "variant"}).getName() != hotend_id: # Save the material that needs to be changed. Multiple changes will be handled by the callback. self._auto_hotends_changed[str(index)] = containers[0].getId() - Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), - catalog.i18nc("@label", - "Do you want to change the materials and hotends to match the material in your printer?"), - catalog.i18nc("@label", - "The materials and / or hotends on your printer were changed. For best results always slice for the materials . hotends that are inserted in your printer."), - buttons=QMessageBox.Yes + QMessageBox.No, - icon=QMessageBox.Question, - callback=self._materialHotendChangedCallback) - - + self._printer_output_devices[0].materialHotendChangedMessage(self._materialHotendChangedCallback) else: Logger.log("w", "No variant found for printer definition %s with id %s" % (self._global_container_stack.getBottom().getId(), hotend_id)) @@ -167,10 +157,7 @@ class MachineManager(QObject): if matching_extruder and matching_extruder.findContainer({"type":"material"}).getMetaDataEntry("GUID") != material_id: # Save the material that needs to be changed. Multiple changes will be handled by the callback. self._auto_materials_changed[str(index)] = containers[0].getId() - Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the materials and hotends to match the material in your printer?"), - catalog.i18nc("@label", "The materials and / or hotends on your printer were changed. For best results always slice for the materials and hotends that are inserted in your printer."), - buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._materialHotendChangedCallback) - + self._printer_output_devices[0].materialHotendChangedMessage(self._materialHotendChangedCallback) else: Logger.log("w", "No material definition found for printer definition %s and GUID %s" % (definition_id, material_id)) @@ -199,14 +186,6 @@ class MachineManager(QObject): if old_index is not None: extruder_manager.setActiveExtruderIndex(old_index) - - - - - - - - def _onGlobalContainerChanged(self): if self._global_container_stack: self._global_container_stack.nameChanged.disconnect(self._onMachineNameChanged) @@ -260,6 +239,20 @@ class MachineManager(QObject): self.activeQualityChanged.emit() def _onPropertyChanged(self, key, property_name): + if property_name == "validationState": + if self._active_stack_valid: + if self._active_container_stack.getProperty(key, "settable_per_extruder"): + changed_validation_state = self._active_container_stack.getProperty(key, property_name) + else: + changed_validation_state = self._global_container_stack.getProperty(key, property_name) + if changed_validation_state in (UM.Settings.ValidatorState.Exception, UM.Settings.ValidatorState.MaximumError, UM.Settings.ValidatorState.MinimumError): + self._active_stack_valid = False + self.activeValidationChanged.emit() + else: + if not self._checkStackForErrors(self._active_container_stack) and not self._checkStackForErrors(self._global_container_stack): + self._active_stack_valid = True + self.activeValidationChanged.emit() + self.activeStackChanged.emit() @pyqtSlot(str) @@ -281,7 +274,7 @@ class MachineManager(QObject): variant_instance_container = self._updateVariantContainer(definition) material_instance_container = self._updateMaterialContainer(definition, variant_instance_container) - quality_instance_container = self._updateQualityContainer(definition, material_instance_container) + quality_instance_container = self._updateQualityContainer(definition, variant_instance_container, material_instance_container) current_settings_instance_container = UM.Settings.InstanceContainer(name + "_current_settings") current_settings_instance_container.addMetaDataEntry("machine", name) @@ -403,6 +396,31 @@ class MachineManager(QObject): return "" + @pyqtProperty("QVariantMap", notify = activeMaterialChanged) + def allActiveMaterialIds(self): + if not self._global_container_stack: + return {} + + result = {} + + for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): + material_container = stack.findContainer(type = "material") + if not material_container: + continue + + result[stack.getId()] = material_container.getId() + + return result + + @pyqtProperty(str, notify=activeQualityChanged) + def activeQualityMaterialId(self): + if self._active_container_stack: + quality = self._active_container_stack.findContainer({"type": "quality"}) + if quality: + return quality.getMetaDataEntry("material") + + return "" + @pyqtProperty(str, notify=activeQualityChanged) def activeQualityName(self): if self._active_container_stack: @@ -425,6 +443,22 @@ class MachineManager(QObject): return quality.getId() return "" + @pyqtProperty(str, notify = activeQualityChanged) + def activeQualityType(self): + if self._global_container_stack: + quality = self._global_container_stack.findContainer(type = "quality") + if quality: + return quality.getMetaDataEntry("quality_type") + return "" + + @pyqtProperty(str, notify = activeQualityChanged) + def activeQualityChangesId(self): + if self._global_container_stack: + changes = self._global_container_stack.findContainer(type = "quality_changes") + if changes: + return changes.getId() + return "" + ## Check if a container is read_only @pyqtSlot(str, result = bool) def isReadOnly(self, container_id): @@ -446,77 +480,13 @@ class MachineManager(QObject): if extruder_stack != self._active_container_stack and extruder_stack.getProperty(key, "value") != new_value: extruder_stack.getTop().setProperty(key, "value", new_value) - @pyqtSlot(str, result=str) - def duplicateContainer(self, container_id): - if not self._active_container_stack: - return "" - containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id) - if containers: - new_name = self._createUniqueName("quality", "", containers[0].getName(), catalog.i18nc("@label", "Custom profile")) - - new_container = containers[0].duplicate(new_name, new_name) - - UM.Settings.ContainerRegistry.getInstance().addContainer(new_container) - - return new_name - - return "" - - @pyqtSlot(str, str) - def renameQualityContainer(self, container_id, new_name): - containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id, type = "quality") - if containers: - new_name = self._createUniqueName("quality", containers[0].getName(), new_name, - catalog.i18nc("@label", "Custom profile")) - - if containers[0].getName() == new_name: - # Nothing to do. - return - - # As we also want the id of the container to be changed (so that profile name is the name of the file - # on disk. We need to create a new instance and remove it (so the old file of the container is removed) - # If we don't do that, we might get duplicates & other weird issues. - new_container = UM.Settings.InstanceContainer("") - new_container.deserialize(containers[0].serialize()) - - # Actually set the name - new_container.setName(new_name) - new_container._id = new_name # Todo: Fix proper id change function for this. - - # Add the "new" container. - UM.Settings.ContainerRegistry.getInstance().addContainer(new_container) - - # Ensure that the renamed profile is saved -before- we remove the old profile. - Application.getInstance().saveSettings() - - # Actually set & remove new / old quality. - self.setActiveQuality(new_name) - self.removeQualityContainer(containers[0].getId()) - - @pyqtSlot(str) - def removeQualityContainer(self, container_id): - containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id) - if not containers or not self._active_container_stack: - return - - # If the container that is being removed is the currently active container, set another machine as the active container - activate_new_container = container_id == self.activeQualityId - - UM.Settings.ContainerRegistry.getInstance().removeContainer(container_id) - - if activate_new_container: - definition_id = "fdmprinter" if not self.filterQualityByMachine else self.activeDefinitionId - containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "quality", definition = definition_id) - if containers: - self.setActiveQuality(containers[0].getId()) - self.activeQualityChanged.emit() - @pyqtSlot(str) def setActiveMaterial(self, material_id): containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = material_id) if not containers or not self._active_container_stack: return + old_variant = self._active_container_stack.findContainer({"type":"variant"}) old_material = self._active_container_stack.findContainer({"type":"material"}) old_quality = self._active_container_stack.findContainer({"type": "quality"}) if old_material: @@ -531,7 +501,7 @@ class MachineManager(QObject): if old_quality: preferred_quality_name = old_quality.getName() - self.setActiveQuality(self._updateQualityContainer(self._global_container_stack.getBottom(), containers[0], preferred_quality_name).id) + self.setActiveQuality(self._updateQualityContainer(self._global_container_stack.getBottom(), old_variant, containers[0], preferred_quality_name).id) else: Logger.log("w", "While trying to set the active material, no material was found to replace.") @@ -568,7 +538,8 @@ class MachineManager(QObject): quality_container = containers[0] elif container_type == "quality_changes": quality_changes_container = containers[0] - containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = quality_changes_container.getMetaDataEntry("quality")) + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers( + quality_type = quality_changes_container.getMetaDataEntry("quality")) if not containers: Logger.log("e", "Could not find quality %s for changes %s, not changing quality", quality_changes_container.getMetaDataEntry("quality"), quality_changes_container.getId()) return @@ -577,14 +548,27 @@ class MachineManager(QObject): Logger.log("e", "Tried to set quality to a container that is not of the right type") return - stacks = [ s for s in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()) ] - stacks.insert(0, self._global_container_stack) + quality_type = quality_container.getMetaDataEntry("quality_type") + if not quality_type: + quality_type = quality_changes_container.getName() - for stack in stacks: + for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): extruder_id = stack.getId() if stack != self._global_container_stack else None - stack_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(name = quality_container.getName(), extruder = extruder_id) + + criteria = { "quality_type": quality_type, "extruder": extruder_id } + + if self._global_container_stack.getMetaDataEntry("has_machine_quality"): + material = stack.findContainer(type = "material") + criteria["material"] = material.getId() + + stack_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria) if not stack_quality: - stack_quality = quality_container + criteria.pop("extruder") + stack_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria) + if not stack_quality: + stack_quality = quality_container + else: + stack_quality = stack_quality[0] else: stack_quality = stack_quality[0] @@ -624,7 +608,11 @@ class MachineManager(QObject): pass elif button == QMessageBox.No: # No, discard the settings in the user profile - self.clearUserSettings() + global_stack = Application.getInstance().getGlobalContainerStack() + for extruder in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()): + extruder.getTop().clear() + + global_stack.getTop().clear() @pyqtProperty(str, notify = activeVariantChanged) def activeVariantName(self): @@ -653,6 +641,16 @@ class MachineManager(QObject): return "" + ## Gets how the active definition calls variants + # Caveat: per-definition-variant-title is currently not translated (though the fallback is) + @pyqtProperty(str, notify = globalContainerChanged) + def activeDefinitionVariantsName(self): + fallback_title = catalog.i18nc("@label", "Nozzle") + if self._global_container_stack: + return self._global_container_stack.getBottom().getMetaDataEntry("variants_name", fallback_title) + + return fallback_title + @pyqtSlot(str, str) def renameMachine(self, machine_id, new_name): containers = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id = machine_id) @@ -776,7 +774,8 @@ class MachineManager(QObject): return self._empty_material_container - def _updateQualityContainer(self, definition, material_container = None, preferred_quality_name = None): + def _updateQualityContainer(self, definition, variant_container, material_container = None, preferred_quality_name = None): + container_registry = UM.Settings.ContainerRegistry.getInstance() search_criteria = { "type": "quality" } if definition.getMetaDataEntry("has_machine_quality"): @@ -787,23 +786,42 @@ class MachineManager(QObject): else: search_criteria["definition"] = "fdmprinter" - if preferred_quality_name: + if preferred_quality_name and preferred_quality_name != "empty": search_criteria["name"] = preferred_quality_name else: preferred_quality = definition.getMetaDataEntry("preferred_quality") if preferred_quality: search_criteria["id"] = preferred_quality - containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) + containers = container_registry.findInstanceContainers(**search_criteria) if containers: return containers[0] + if "material" in search_criteria: + # If a quality for this specific material cannot be found, try finding qualities for a generic version of the material + material_search_criteria = { "type": "material", "material": material_container.getMetaDataEntry("material"), "color_name": "Generic" } + if definition.getMetaDataEntry("has_machine_quality"): + material_search_criteria["definition"] = definition.id + + if definition.getMetaDataEntry("has_variants") and variant_container: + material_search_criteria["variant"] = variant_container.id + else: + material_search_criteria["definition"] = "fdmprinter" + + material_containers = container_registry.findInstanceContainers(**material_search_criteria) + if material_containers: + search_criteria["material"] = material_containers[0].getId() + + containers = container_registry.findInstanceContainers(**search_criteria) + if containers: + return containers[0] + if "name" in search_criteria or "id" in search_criteria: # If a quality by this name can not be found, try a wider set of search criteria search_criteria.pop("name", None) search_criteria.pop("id", None) - containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) + containers = container_registry.findInstanceContainers(**search_criteria) if containers: return containers[0] diff --git a/cura/Settings/QualitySettingsModel.py b/cura/Settings/QualitySettingsModel.py new file mode 100644 index 0000000000..4362dd51d8 --- /dev/null +++ b/cura/Settings/QualitySettingsModel.py @@ -0,0 +1,173 @@ +# Copyright (c) 2016 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +import collections + +from PyQt5.QtCore import pyqtProperty, pyqtSignal, Qt + +import UM.Application +import UM.Logger +import UM.Qt +import UM.Settings + + +class QualitySettingsModel(UM.Qt.ListModel.ListModel): + KeyRole = Qt.UserRole + 1 + LabelRole = Qt.UserRole + 2 + UnitRole = Qt.UserRole + 3 + ProfileValueRole = Qt.UserRole + 4 + UserValueRole = Qt.UserRole + 5 + CategoryRole = Qt.UserRole + 6 + + def __init__(self, parent = None): + super().__init__(parent = parent) + + self._extruder_id = None + self._quality = None + self._material = None + + self.addRoleName(self.KeyRole, "key") + self.addRoleName(self.LabelRole, "label") + self.addRoleName(self.UnitRole, "unit") + self.addRoleName(self.ProfileValueRole, "profile_value") + self.addRoleName(self.UserValueRole, "user_value") + self.addRoleName(self.CategoryRole, "category") + + def setExtruderId(self, extruder_id): + if extruder_id != self._extruder_id: + self._extruder_id = extruder_id + self._update() + self.extruderIdChanged.emit() + + extruderIdChanged = pyqtSignal() + @pyqtProperty(str, fset = setExtruderId, notify = extruderIdChanged) + def extruderId(self): + return self._extruder_id + + def setQuality(self, quality): + if quality != self._quality: + self._quality = quality + self._update() + self.qualityChanged.emit() + + qualityChanged = pyqtSignal() + @pyqtProperty(str, fset = setQuality, notify = qualityChanged) + def quality(self): + return self._quality + + def setMaterial(self, material): + if material != self._material: + self._material = material + self._update() + self.materialChanged.emit() + + materialChanged = pyqtSignal() + @pyqtProperty(str, fset = setMaterial, notify = materialChanged) + def material(self): + return self._material + + def _update(self): + if not self._quality: + return + + self.clear() + + settings = collections.OrderedDict() + definition_container = UM.Application.getInstance().getGlobalContainerStack().getBottom() + + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = self._quality) + if not containers: + UM.Logger.log("w", "Could not find a quality container with id %s", self._quality) + return + + quality_container = None + quality_changes_container = None + + if containers[0].getMetaDataEntry("type") == "quality": + quality_container = containers[0] + else: + quality_changes_container = containers[0] + + criteria = { + "type": "quality", + "quality_type": quality_changes_container.getMetaDataEntry("quality"), + "definition": quality_changes_container.getDefinition().getId() + } + + if self._material: + criteria["material"] = self._material + + quality_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria) + if not quality_container: + UM.Logger.log("w", "Could not find a quality container matching quality changes %s", quality_changes_container.getId()) + return + quality_container = quality_container[0] + + quality_type = quality_container.getMetaDataEntry("quality_type") + definition_id = quality_container.getDefinition().getId() + + criteria = { "type": "quality", "quality_type": quality_type, "definition": definition_id } + + if self._material: + criteria["material"] = self._material + + criteria["extruder"] = self._extruder_id + + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria) + if not containers: + # Try again, this time without extruder + new_criteria = criteria.copy() + new_criteria.pop("extruder") + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**new_criteria) + + if not containers: + # Try again, this time without material + criteria.pop("material") + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria) + + if not containers: + # Try again, this time without material or extruder + criteria.pop("extruder") # "material" has already been popped + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria) + + if not containers: + UM.Logger.log("Could not find any quality containers matching the search criteria %s" % str(criteria)) + return + + if quality_changes_container: + criteria = {"type": "quality_changes", "quality": quality_type, "extruder": self._extruder_id, "definition": definition_id } + changes = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria) + if changes: + containers.extend(changes) + + current_category = "" + for definition in definition_container.findDefinitions(): + if definition.type == "category": + current_category = definition.label + continue + + profile_value = None + for container in containers: + new_value = container.getProperty(definition.key, "value") + if new_value: + profile_value = new_value + + user_value = None + if not self._extruder_id: + user_value = UM.Application.getInstance().getGlobalContainerStack().getTop().getProperty(definition.key, "value") + else: + extruder_stack = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id = self._extruder_id) + if extruder_stack: + user_value = extruder_stack[0].getTop().getProperty(definition.key, "value") + + if not profile_value and not user_value: + continue + + self.appendItem({ + "key": definition.key, + "label": definition.label, + "unit": definition.unit, + "profile_value": "" if profile_value is None else str(profile_value), # it is for display only + "user_value": "" if user_value is None else str(user_value), + "category": current_category + }) diff --git a/cura/Settings/__init__.py b/cura/Settings/__init__.py index da8f36c040..5daa00c84f 100644 --- a/cura/Settings/__init__.py +++ b/cura/Settings/__init__.py @@ -10,3 +10,4 @@ from .ExtrudersModel import ExtrudersModel from .MachineManager import MachineManager from .MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler from .SettingOverrideDecorator import SettingOverrideDecorator +from .QualitySettingsModel import QualitySettingsModel diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 70dcd300a4..b37814f28b 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -195,9 +195,8 @@ class CuraEngineBackend(Backend): self.slicingCancelled.emit() self.processingProgress.emit(0) Logger.log("d", "Attempting to kill the engine process") - + self._createSocket() # Ensure that we have a fresh socket. if Application.getInstance().getCommandLineOption("external-backend", False): - self._createSocket() return if self._process is not None: @@ -206,6 +205,7 @@ class CuraEngineBackend(Backend): self._process.terminate() Logger.log("d", "Engine process is killed. Received return code %s", self._process.wait()) self._process = None + except Exception as e: # terminating a process that is already terminating causes an exception, silently ignore this. Logger.log("d", "Exception occurred while trying to kill the engine %s", str(e)) @@ -236,7 +236,7 @@ class CuraEngineBackend(Backend): if job.getResult() == StartSliceJob.StartJobResult.NothingToSlice: if Application.getInstance().getPlatformActivity: - self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. No suitable objects found.")) + self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. No suitable models found.")) self._error_message.show() self.backendStateChange.emit(BackendState.Error) else: diff --git a/plugins/LegacyProfileReader/DictionaryOfDoom.json b/plugins/LegacyProfileReader/DictionaryOfDoom.json index 471604ddbc..db0d26b8e4 100644 --- a/plugins/LegacyProfileReader/DictionaryOfDoom.json +++ b/plugins/LegacyProfileReader/DictionaryOfDoom.json @@ -1,6 +1,6 @@ { "source_version": "15.04", - "target_version": 1, + "target_version": 2, "translation": { "machine_nozzle_size": "nozzle_size", @@ -21,7 +21,7 @@ "retraction_amount": "retraction_amount", "retraction_speed": "retraction_speed", "retraction_min_travel": "retraction_min_travel", - "retraction_hop": "retraction_hop", + "retraction_hop_enabled": "retraction_hop != 0", "speed_print": "print_speed", "speed_infill": "infill_speed if (float(infill_speed) != 0) else print_speed", "speed_wall_0": "inset0_speed if (float(inset0_speed) != 0) else print_speed", @@ -29,7 +29,7 @@ "speed_topbottom": "solidarea_speed if (float(solidarea_speed) != 0) else print_speed", "speed_travel": "travel_speed if (float(travel_speed) != 0) else travel_speed", "speed_layer_0": "bottom_layer_speed", - "retraction_combing": "True if (retraction_combing == \"All\" or retraction_combing == \"No Skin\") else False", + "retraction_combing": "\"all\" if retraction_combing == \"All\" else \"noskin\" if retraction_combing == \"No Skin\" else \"off\"", "cool_fan_enabled": "fan_enabled", "cool_fan_speed_min": "fan_speed", "cool_fan_speed_max": "fan_speed_max", @@ -66,7 +66,7 @@ "meshfix_union_all_remove_holes": "fix_horrible_union_all_type_b", "meshfix_extensive_stitching": "fix_horrible_extensive_stitching", "meshfix_keep_open_polygons": "fix_horrible_use_open_bits", - "magic_mesh_surface_mode": "simple_mode", + "magic_mesh_surface_mode": "\"surface\" if simple_mode else \"normal\"", "magic_spiralize": "spiralize", "prime_tower_enable": "wipe_tower", "prime_tower_size": "math.sqrt(float(wipe_tower_volume) / float(layer_height))", diff --git a/plugins/LegacyProfileReader/LegacyProfileReader.py b/plugins/LegacyProfileReader/LegacyProfileReader.py index 19154c9c5a..a3e4e60b6a 100644 --- a/plugins/LegacyProfileReader/LegacyProfileReader.py +++ b/plugins/LegacyProfileReader/LegacyProfileReader.py @@ -111,7 +111,8 @@ class LegacyProfileReader(ProfileReader): if "translation" not in dict_of_doom: Logger.log("e", "Dictionary of Doom has no translation. Is it the correct JSON file?") return None - current_printer = Application.getInstance().getGlobalContainerStack().findContainer({ }, DefinitionContainer) + current_printer_definition = Application.getInstance().getGlobalContainerStack().getBottom() + profile.setDefinition(current_printer_definition) for new_setting in dict_of_doom["translation"]: #Evaluate all new settings that would get a value from the translations. old_setting_expression = dict_of_doom["translation"][new_setting] compiled = compile(old_setting_expression, new_setting, "eval") @@ -121,10 +122,13 @@ class LegacyProfileReader(ProfileReader): except Exception: #Probably some setting name that was missing or something else that went wrong in the ini file. Logger.log("w", "Setting " + new_setting + " could not be set because the evaluation failed. Something is probably missing from the imported legacy profile.") continue - if new_value != value_using_defaults and current_printer.findDefinitions(key = new_setting).default_value != new_value: #Not equal to the default in the new Cura OR the default in the legacy Cura. - profile.setSettingValue(new_setting, new_value) #Store the setting in the profile! + definitions = current_printer_definition.findDefinitions(key = new_setting) + if definitions: + if new_value != value_using_defaults and definitions[0].default_value != new_value: # Not equal to the default in the new Cura OR the default in the legacy Cura. + profile.setProperty(new_setting, "value", new_value) # Store the setting in the profile! - if len(profile.getChangedSettings()) == 0: + if len(profile.getAllKeys()) == 0: Logger.log("i", "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile.") profile.setDirty(True) + profile.addMetaDataEntry("type", "quality") return profile \ No newline at end of file diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.py b/plugins/MachineSettingsAction/MachineSettingsAction.py new file mode 100644 index 0000000000..a37aa9c5cb --- /dev/null +++ b/plugins/MachineSettingsAction/MachineSettingsAction.py @@ -0,0 +1,93 @@ +# Copyright (c) 2016 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +from PyQt5.QtCore import pyqtSlot + +from cura.MachineAction import MachineAction +import cura.Settings.CuraContainerRegistry + +import UM.Application +import UM.Settings.InstanceContainer +import UM.Settings.DefinitionContainer +import UM.Logger + +import UM.i18n +catalog = UM.i18n.i18nCatalog("cura") + +class MachineSettingsAction(MachineAction): + def __init__(self, parent = None): + super().__init__("MachineSettingsAction", catalog.i18nc("@action", "Machine Settings")) + self._qml_url = "MachineSettingsAction.qml" + + cura.Settings.CuraContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded) + + def _reset(self): + global_container_stack = UM.Application.getInstance().getGlobalContainerStack() + if global_container_stack: + variant = global_container_stack.findContainer({"type": "variant"}) + if variant and variant.getId() == "empty_variant": + variant_index = global_container_stack.getContainerIndex(variant) + self._createVariant(global_container_stack, variant_index) + + def _createVariant(self, global_container_stack, variant_index): + # Create and switch to a variant to store the settings in + new_variant = UM.Settings.InstanceContainer(global_container_stack.getName() + "_variant") + new_variant.addMetaDataEntry("type", "variant") + new_variant.setDefinition(global_container_stack.getBottom()) + UM.Settings.ContainerRegistry.getInstance().addContainer(new_variant) + global_container_stack.replaceContainer(variant_index, new_variant) + + def _onContainerAdded(self, container): + # Add this action as a supported action to all machine definitions + if isinstance(container, UM.Settings.DefinitionContainer) and container.getMetaDataEntry("type") == "machine": + if container.getProperty("machine_extruder_count", "value") > 1: + # Multiextruder printers are not currently supported + UM.Logger.log("d", "Not attaching MachineSettingsAction to %s; Multi-extrusion printers are not supported", container.getId()) + return + if container.getMetaDataEntry("has_variants", False): + # Machines that use variants are not currently supported + UM.Logger.log("d", "Not attaching MachineSettingsAction to %s; Machines that use variants are not supported", container.getId()) + return + + UM.Application.getInstance().getMachineActionManager().addSupportedAction(container.getId(), self.getKey()) + + @pyqtSlot() + def forceUpdate(self): + # Force rebuilding the build volume by reloading the global container stack. + # This is a bit of a hack, but it seems quick enough. + UM.Application.getInstance().globalContainerStackChanged.emit() + + @pyqtSlot() + def updateHasMaterialsMetadata(self): + # Updates the has_materials metadata flag after switching gcode flavor + global_container_stack = UM.Application.getInstance().getGlobalContainerStack() + if global_container_stack: + definition = global_container_stack.getBottom() + if definition.getProperty("machine_gcode_flavor", "value") == "UltiGCode" and not definition.getMetaDataEntry("has_materials", False): + has_materials = global_container_stack.getProperty("machine_gcode_flavor", "value") != "UltiGCode" + + material_container = global_container_stack.findContainer({"type": "material"}) + material_index = global_container_stack.getContainerIndex(material_container) + + if has_materials: + if "has_materials" in global_container_stack.getMetaData(): + global_container_stack.setMetaDataEntry("has_materials", True) + else: + global_container_stack.addMetaDataEntry("has_materials", True) + + # Set the material container to a sane default + if material_container.getId() == "empty_material": + search_criteria = { "type": "material", "definition": "fdmprinter", "id": "*pla*" } + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) + if containers: + global_container_stack.replaceContainer(material_index, containers[0]) + else: + # The metadata entry is stored in an ini, and ini files are parsed as strings only. + # Because any non-empty string evaluates to a boolean True, we have to remove the entry to make it False. + if "has_materials" in global_container_stack.getMetaData(): + global_container_stack.removeMetaDataEntry("has_materials") + + empty_material = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_material")[0] + global_container_stack.replaceContainer(material_index, empty_material) + + UM.Application.getInstance().globalContainerStackChanged.emit() \ No newline at end of file diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.qml b/plugins/MachineSettingsAction/MachineSettingsAction.qml new file mode 100644 index 0000000000..3c1839e07f --- /dev/null +++ b/plugins/MachineSettingsAction/MachineSettingsAction.qml @@ -0,0 +1,476 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + + +Cura.MachineAction +{ + anchors.fill: parent; + Item + { + id: bedLevelMachineAction + anchors.fill: parent; + + UM.I18nCatalog { id: catalog; name: "cura"; } + + Label + { + id: pageTitle + width: parent.width + text: catalog.i18nc("@title", "Machine Settings") + wrapMode: Text.WordWrap + font.pointSize: 18; + } + Label + { + id: pageDescription + anchors.top: pageTitle.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height + width: parent.width + wrapMode: Text.WordWrap + text: catalog.i18nc("@label", "Please enter the correct settings for your printer below:") + } + + Column + { + height: parent.height - y + width: parent.width - UM.Theme.getSize("default_margin").width + spacing: UM.Theme.getSize("default_margin").height + + anchors.left: parent.left + anchors.top: pageDescription.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height + + Row + { + width: parent.width + spacing: UM.Theme.getSize("default_margin").height + + Column + { + width: parent.width / 2 + spacing: UM.Theme.getSize("default_margin").height + + Label + { + text: catalog.i18nc("@label", "Printer Settings") + font.bold: true + } + + Grid + { + columns: 3 + columnSpacing: UM.Theme.getSize("default_margin").width + + Label + { + text: catalog.i18nc("@label", "X (Width)") + } + TextField + { + id: buildAreaWidthField + text: machineWidthProvider.properties.value + validator: RegExpValidator { regExp: /[0-9]{0,6}/ } + onEditingFinished: { machineWidthProvider.setPropertyValue("value", text); manager.forceUpdate() } + } + Label + { + text: catalog.i18nc("@label", "mm") + } + + Label + { + text: catalog.i18nc("@label", "Y (Depth)") + } + TextField + { + id: buildAreaDepthField + text: machineDepthProvider.properties.value + validator: RegExpValidator { regExp: /[0-9]{0,6}/ } + onEditingFinished: { machineDepthProvider.setPropertyValue("value", text); manager.forceUpdate() } + } + Label + { + text: catalog.i18nc("@label", "mm") + } + + Label + { + text: catalog.i18nc("@label", "Z (Height)") + } + TextField + { + id: buildAreaHeightField + text: machineHeightProvider.properties.value + validator: RegExpValidator { regExp: /[0-9]{0,6}/ } + onEditingFinished: { machineHeightProvider.setPropertyValue("value", text); manager.forceUpdate() } + } + Label + { + text: catalog.i18nc("@label", "mm") + } + } + + Column + { + CheckBox + { + id: heatedBedCheckBox + text: catalog.i18nc("@option:check", "Heated Bed") + checked: String(machineHeatedBedProvider.properties.value).toLowerCase() != 'false' + onClicked: machineHeatedBedProvider.setPropertyValue("value", checked) + } + CheckBox + { + id: centerIsZeroCheckBox + text: catalog.i18nc("@option:check", "Machine Center is Zero") + checked: String(machineCenterIsZeroProvider.properties.value).toLowerCase() != 'false' + onClicked: machineCenterIsZeroProvider.setPropertyValue("value", checked) + } + } + + Row + { + spacing: UM.Theme.getSize("default_margin").width + + Label + { + text: catalog.i18nc("@label", "GCode Flavor") + } + + ComboBox + { + model: ["RepRap (Marlin/Sprinter)", "UltiGCode"] + currentIndex: machineGCodeFlavorProvider.properties.value != model[1] ? 0 : 1 + onActivated: + { + machineGCodeFlavorProvider.setPropertyValue("value", model[index]); + manager.updateHasMaterialsMetadata(); + } + } + } + } + + Column + { + width: parent.width / 2 + spacing: UM.Theme.getSize("default_margin").height + + Label + { + text: catalog.i18nc("@label", "Printhead Settings") + font.bold: true + } + + Grid + { + columns: 3 + columnSpacing: UM.Theme.getSize("default_margin").width + + Label + { + text: catalog.i18nc("@label", "X min") + } + TextField + { + id: printheadXMinField + text: getHeadPolygonCoord("x", "min") + validator: RegExpValidator { regExp: /[0-9]{0,6}/ } + onEditingFinished: setHeadPolygon() + } + Label + { + text: catalog.i18nc("@label", "mm") + } + + Label + { + text: catalog.i18nc("@label", "Y min") + } + TextField + { + id: printheadYMinField + text: getHeadPolygonCoord("y", "min") + validator: RegExpValidator { regExp: /[0-9]{0,6}/ } + onEditingFinished: setHeadPolygon() + } + Label + { + text: catalog.i18nc("@label", "mm") + } + + Label + { + text: catalog.i18nc("@label", "X max") + } + TextField + { + id: printheadXMaxField + text: getHeadPolygonCoord("x", "max") + validator: RegExpValidator { regExp: /[0-9]{0,6}/ } + onEditingFinished: setHeadPolygon() + } + Label + { + text: catalog.i18nc("@label", "mm") + } + + Label + { + text: catalog.i18nc("@label", "Y max") + } + TextField + { + id: printheadYMaxField + text: getHeadPolygonCoord("y", "max") + validator: RegExpValidator { regExp: /[0-9]{0,6}/ } + onEditingFinished: setHeadPolygon() + } + Label + { + text: catalog.i18nc("@label", "mm") + } + + Item { width: UM.Theme.getSize("default_margin").width; height: UM.Theme.getSize("default_margin").height } + Item { width: UM.Theme.getSize("default_margin").width; height: UM.Theme.getSize("default_margin").height } + Item { width: UM.Theme.getSize("default_margin").width; height: UM.Theme.getSize("default_margin").height } + + Label + { + text: catalog.i18nc("@label", "Gantry height") + } + TextField + { + id: gantryHeightField + text: gantryHeightProvider.properties.value + validator: RegExpValidator { regExp: /[0-9\.]{0,6}/ } + onEditingFinished: { gantryHeightProvider.setPropertyValue("value", text) } + } + Label + { + text: catalog.i18nc("@label", "mm") + } + + Item { width: UM.Theme.getSize("default_margin").width; height: UM.Theme.getSize("default_margin").height } + Item { width: UM.Theme.getSize("default_margin").width; height: UM.Theme.getSize("default_margin").height } + Item { width: UM.Theme.getSize("default_margin").width; height: UM.Theme.getSize("default_margin").height } + + Label + { + text: catalog.i18nc("@label", "Nozzle size") + } + TextField + { + id: nozzleSizeField + text: machineNozzleSizeProvider.properties.value + validator: RegExpValidator { regExp: /[0-9\.]{0,6}/ } + onEditingFinished: { machineNozzleSizeProvider.setPropertyValue("value", text) } + } + Label + { + text: catalog.i18nc("@label", "mm") + } + } + } + } + + Row + { + spacing: UM.Theme.getSize("default_margin").width + anchors.left: parent.left + anchors.right: parent.right + height: parent.height - y + Column + { + height: parent.height + width: parent.width / 2 + Label + { + text: catalog.i18nc("@label", "Start Gcode") + } + TextArea + { + id: machineStartGcodeField + width: parent.width + height: parent.height - y + text: machineStartGcodeProvider.properties.value + onActiveFocusChanged: + { + if(!activeFocus) + { + machineStartGcodeProvider.setPropertyValue("value", machineStartGcodeField.text) + } + } + } + } + Column { + height: parent.height + width: parent.width / 2 + Label + { + text: catalog.i18nc("@label", "End Gcode") + } + TextArea + { + id: machineEndGcodeField + width: parent.width + height: parent.height - y + text: machineEndGcodeProvider.properties.value + onActiveFocusChanged: + { + if(!activeFocus) + { + machineEndGcodeProvider.setPropertyValue("value", machineEndGcodeField.text) + } + } + } + } + } + } + } + + function getHeadPolygonCoord(axis, minMax) + { + var polygon = JSON.parse(machineHeadPolygonProvider.properties.value); + var item = (axis == "x") ? 0 : 1 + var result = polygon[0][item]; + for(var i = 1; i < polygon.length; i++) { + if (minMax == "min") { + result = Math.min(result, polygon[i][item]); + } else { + result = Math.max(result, polygon[i][item]); + } + } + return Math.abs(result); + } + + function setHeadPolygon() + { + var polygon = []; + polygon.push([-parseFloat(printheadXMinField.text), parseFloat(printheadYMaxField.text)]); + polygon.push([-parseFloat(printheadXMinField.text),-parseFloat(printheadYMinField.text)]); + polygon.push([ parseFloat(printheadXMaxField.text), parseFloat(printheadYMaxField.text)]); + polygon.push([ parseFloat(printheadXMaxField.text),-parseFloat(printheadYMinField.text)]); + machineHeadPolygonProvider.setPropertyValue("value", JSON.stringify(polygon)); + manager.forceUpdate(); + } + + UM.SettingPropertyProvider + { + id: machineWidthProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_width" + watchedProperties: [ "value" ] + storeIndex: 4 + } + + UM.SettingPropertyProvider + { + id: machineDepthProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_depth" + watchedProperties: [ "value" ] + storeIndex: 4 + } + + UM.SettingPropertyProvider + { + id: machineHeightProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_height" + watchedProperties: [ "value" ] + storeIndex: 4 + } + + UM.SettingPropertyProvider + { + id: machineHeatedBedProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_heated_bed" + watchedProperties: [ "value" ] + storeIndex: 4 + } + + UM.SettingPropertyProvider + { + id: machineCenterIsZeroProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_center_is_zero" + watchedProperties: [ "value" ] + storeIndex: 4 + } + + UM.SettingPropertyProvider + { + id: machineGCodeFlavorProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_gcode_flavor" + watchedProperties: [ "value" ] + storeIndex: 4 + } + + UM.SettingPropertyProvider + { + id: machineNozzleSizeProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_nozzle_size" + watchedProperties: [ "value" ] + storeIndex: 4 + } + + UM.SettingPropertyProvider + { + id: gantryHeightProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "gantry_height" + watchedProperties: [ "value" ] + storeIndex: 4 + } + + UM.SettingPropertyProvider + { + id: machineHeadPolygonProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_head_with_fans_polygon" + watchedProperties: [ "value" ] + storeIndex: 4 + } + + + UM.SettingPropertyProvider + { + id: machineStartGcodeProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_start_gcode" + watchedProperties: [ "value" ] + storeIndex: 4 + } + + UM.SettingPropertyProvider + { + id: machineEndGcodeProvider + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_end_gcode" + watchedProperties: [ "value" ] + storeIndex: 4 + } + +} \ No newline at end of file diff --git a/plugins/MachineSettingsAction/__init__.py b/plugins/MachineSettingsAction/__init__.py new file mode 100644 index 0000000000..7d8b253d33 --- /dev/null +++ b/plugins/MachineSettingsAction/__init__.py @@ -0,0 +1,21 @@ +# Copyright (c) 2016 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +from . import MachineSettingsAction + +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") + +def getMetaData(): + return { + "plugin": { + "name": catalog.i18nc("@label", "Machine Settings action"), + "author": "fieldOfView", + "version": "1.0", + "description": catalog.i18nc("@info:whatsthis", "Provides a way to change machine settings (such as build volume, nozzle size, etc)"), + "api": 3 + } + } + +def register(app): + return { "machine_action": MachineSettingsAction.MachineSettingsAction() } diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index 0930ae568e..6c2411604e 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -31,7 +31,7 @@ Item { spacing: UM.Theme.getSize("default_margin").width Label { - text: catalog.i18nc("@label", "Print object with") + text: catalog.i18nc("@label", "Print model with") anchors.verticalCenter: extruderSelector.verticalCenter color: UM.Theme.getColor("setting_control_text") @@ -154,107 +154,114 @@ Item { Column { spacing: UM.Theme.getSize("default_lining").height - - Repeater + // This is to ensure that the panel is first increasing in size up to 200 and then shows a scrollbar. + // It kinda looks ugly otherwise (big panel, no content on it) + height: contents.count * UM.Theme.getSize("section").height < 200 ? contents.count * UM.Theme.getSize("section").height : 200 + ScrollView { - id: contents - height: childrenRect.height; - - model: UM.SettingDefinitionsModel + height: parent.height + width: UM.Theme.getSize("setting").width + UM.Theme.getSize("setting").height + style: UM.Theme.styles.scrollview + ListView { - id: addedSettingsModel; - containerId: Cura.MachineManager.activeDefinitionId - expanded: [ "*" ] + id: contents - visibilityHandler: Cura.PerObjectSettingVisibilityHandler + model: UM.SettingDefinitionsModel { - selectedObjectId: UM.ActiveTool.properties.getValue("SelectedObjectId") - } - } + id: addedSettingsModel; + containerId: Cura.MachineManager.activeDefinitionId + expanded: [ "*" ] - delegate: Row - { - Loader - { - id: settingLoader - width: UM.Theme.getSize("setting").width - height: UM.Theme.getSize("section").height - - property var definition: model - property var settingDefinitionsModel: addedSettingsModel - property var propertyProvider: provider - - //Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989 - //In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes, - //causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely. - asynchronous: model.type != "enum" && model.type != "extruder" - - onLoaded: { - settingLoader.item.showRevertButton = false - settingLoader.item.showInheritButton = false - settingLoader.item.showLinkedSettingIcon = false - settingLoader.item.doDepthIndentation = false - settingLoader.item.doQualityUserSettingEmphasis = false - } - - sourceComponent: + visibilityHandler: Cura.PerObjectSettingVisibilityHandler { - switch(model.type) - { - case "int": - return settingTextField - case "float": - return settingTextField - case "enum": - return settingComboBox - case "extruder": - return settingExtruder - case "bool": - return settingCheckBox - case "str": - return settingTextField - case "category": - return settingCategory - default: - return settingUnknown + selectedObjectId: UM.ActiveTool.properties.getValue("SelectedObjectId") + } + } + + delegate: Row + { + Loader + { + id: settingLoader + width: UM.Theme.getSize("setting").width + height: UM.Theme.getSize("section").height + + property var definition: model + property var settingDefinitionsModel: addedSettingsModel + property var propertyProvider: provider + + //Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989 + //In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes, + //causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely. + asynchronous: model.type != "enum" && model.type != "extruder" + + onLoaded: { + settingLoader.item.showRevertButton = false + settingLoader.item.showInheritButton = false + settingLoader.item.showLinkedSettingIcon = false + settingLoader.item.doDepthIndentation = false + settingLoader.item.doQualityUserSettingEmphasis = false } - } - } - Button - { - width: UM.Theme.getSize("setting").height / 2; - height: UM.Theme.getSize("setting").height; - - onClicked: addedSettingsModel.setVisible(model.key, false); - - style: ButtonStyle - { - background: Item + sourceComponent: { - UM.RecolorImage + switch(model.type) { - anchors.verticalCenter: parent.verticalCenter - width: parent.width - height: parent.height / 2 - sourceSize.width: width - sourceSize.height: width - color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button") - source: UM.Theme.getIcon("minus") + case "int": + return settingTextField + case "float": + return settingTextField + case "enum": + return settingComboBox + case "extruder": + return settingExtruder + case "bool": + return settingCheckBox + case "str": + return settingTextField + case "category": + return settingCategory + default: + return settingUnknown } } } - } - UM.SettingPropertyProvider - { - id: provider + Button + { + width: UM.Theme.getSize("setting").height / 2; + height: UM.Theme.getSize("setting").height; - containerStackId: UM.ActiveTool.properties.getValue("ContainerID") - key: model.key - watchedProperties: [ "value", "enabled", "validationState" ] - storeIndex: 0 - removeUnusedValue: false + onClicked: addedSettingsModel.setVisible(model.key, false); + + style: ButtonStyle + { + background: Item + { + UM.RecolorImage + { + anchors.verticalCenter: parent.verticalCenter + width: parent.width + height: parent.height / 2 + sourceSize.width: width + sourceSize.height: width + color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button") + source: UM.Theme.getIcon("minus") + } + } + } + } + + UM.SettingPropertyProvider + { + id: provider + + containerStackId: UM.ActiveTool.properties.getValue("ContainerID") + key: model.key + watchedProperties: [ "value", "enabled", "validationState" ] + storeIndex: 0 + removeUnusedValue: false + } } } } @@ -306,7 +313,7 @@ Item { UM.Dialog { id: settingPickDialog - title: catalog.i18nc("@title:window", "Select Settings to Customize for this object") + title: catalog.i18nc("@title:window", "Select Settings to Customize for this model") width: screenScaleFactor * 360; property string labelFilter: "" diff --git a/plugins/PerObjectSettingsTool/__init__.py b/plugins/PerObjectSettingsTool/__init__.py index 1779556ade..08b8de1452 100644 --- a/plugins/PerObjectSettingsTool/__init__.py +++ b/plugins/PerObjectSettingsTool/__init__.py @@ -11,15 +11,15 @@ i18n_catalog = i18nCatalog("cura") def getMetaData(): return { "plugin": { - "name": i18n_catalog.i18nc("@label", "Per Object Settings Tool"), + "name": i18n_catalog.i18nc("@label", "Per Model Settings Tool"), "author": "Ultimaker", "version": "1.0", - "description": i18n_catalog.i18nc("@info:whatsthis", "Provides the Per Object Settings."), + "description": i18n_catalog.i18nc("@info:whatsthis", "Provides the Per Model Settings."), "api": 3 }, "tool": { - "name": i18n_catalog.i18nc("@label", "Per Object Settings"), - "description": i18n_catalog.i18nc("@info:tooltip", "Configure Per Object Settings"), + "name": i18n_catalog.i18nc("@label", "Per Model Settings"), + "description": i18n_catalog.i18nc("@info:tooltip", "Configure Per Model Settings"), "icon": "setting_per_object", "tool_panel": "PerObjectSettingsPanel.qml", "weight": 3 diff --git a/plugins/USBPrinting/FirmwareUpdateWindow.qml b/plugins/USBPrinting/FirmwareUpdateWindow.qml index d55541f36d..b42cff114f 100644 --- a/plugins/USBPrinting/FirmwareUpdateWindow.qml +++ b/plugins/USBPrinting/FirmwareUpdateWindow.qml @@ -32,20 +32,44 @@ UM.Dialog } text: { - if (manager.firmwareUpdateCompleteStatus) + if (manager.errorCode == 0) { - //: Firmware update status label - return catalog.i18nc("@label","Firmware update completed.") - } - else if (manager.progress == 0) - { - //: Firmware update status label - return catalog.i18nc("@label","Starting firmware update, this may take a while.") + if (manager.firmwareUpdateCompleteStatus) + { + //: Firmware update status label + return catalog.i18nc("@label","Firmware update completed.") + } + else if (manager.progress == 0) + { + //: Firmware update status label + return catalog.i18nc("@label","Starting firmware update, this may take a while.") + } + else + { + //: Firmware update status label + return catalog.i18nc("@label","Updating firmware.") + } } else { - //: Firmware update status label - return catalog.i18nc("@label","Updating firmware.") + switch (manager.errorCode) + { + case 1: + //: Firmware update status label + return catalog.i18nc("@label","Firmware update failed due to an unknown error.") + case 2: + //: Firmware update status label + return catalog.i18nc("@label","Firmware update failed due to an communication error.") + case 3: + //: Firmware update status label + return catalog.i18nc("@label","Firmware update failed due to an input/output error.") + case 4: + //: Firmware update status label + return catalog.i18nc("@label","Firmware update failed due to missing firmware.") + default: + //: Firmware update status label + return catalog.i18nc("@label", "Unknown error code: %1").arg(manager.errorCode) + } } } diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index af90961ff9..920a71d40f 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -14,7 +14,7 @@ from UM.Logger import Logger from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState from UM.Message import Message -from PyQt5.QtCore import QUrl, pyqtSlot, pyqtSignal +from PyQt5.QtCore import QUrl, pyqtSlot, pyqtSignal, pyqtProperty from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") @@ -90,6 +90,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._firmware_update_finished = False self._error_message = None + self._error_code = 0 onError = pyqtSignal() @@ -173,6 +174,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): ## Private function (threaded) that actually uploads the firmware. def _updateFirmware(self): + self._error_code = 0 self.setProgress(0, 100) self._firmware_update_finished = False @@ -182,6 +184,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): if len(hex_file) == 0: Logger.log("e", "Unable to read provided hex file. Could not update firmware") + self._updateFirmwareFailedMissingFirmware() return programmer = stk500v2.Stk500v2() @@ -197,6 +200,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): if not programmer.isConnected(): Logger.log("e", "Unable to connect with serial. Could not update firmware") + self._updateFirmwareFailedCommunicationError() return self._updating_firmware = True @@ -204,17 +208,57 @@ class USBPrinterOutputDevice(PrinterOutputDevice): try: programmer.programChip(hex_file) self._updating_firmware = False + except serial.SerialException as e: + Logger.log("e", "SerialException while trying to update firmware: <%s>" %(repr(e))) + self._updateFirmwareFailedIOError() + return except Exception as e: - Logger.log("e", "Exception while trying to update firmware %s" %e) - self._updating_firmware = False + Logger.log("e", "Exception while trying to update firmware: <%s>" %(repr(e))) + self._updateFirmwareFailedUnknown() return programmer.close() + self._updateFirmwareCompletedSucessfully() + return + + ## Private function which makes sure that firmware update process has failed by missing firmware + def _updateFirmwareFailedMissingFirmware(self): + return self._updateFirmwareFailedCommon(4) + + ## Private function which makes sure that firmware update process has failed by an IO error + def _updateFirmwareFailedIOError(self): + return self._updateFirmwareFailedCommon(3) + + ## Private function which makes sure that firmware update process has failed by a communication problem + def _updateFirmwareFailedCommunicationError(self): + return self._updateFirmwareFailedCommon(2) + + ## Private function which makes sure that firmware update process has failed by an unknown error + def _updateFirmwareFailedUnknown(self): + return self._updateFirmwareFailedCommon(1) + + ## Private common function which makes sure that firmware update process has completed/ended with a set progress state + def _updateFirmwareFailedCommon(self, code): + if not code: + raise Exception("Error code not set!") + + self._error_code = code + + self._firmware_update_finished = True + self.resetFirmwareUpdate(update_has_finished = True) + self.progressChanged.emit() + self.firmwareUpdateComplete.emit() + + return + + ## Private function which makes sure that firmware update process has successfully completed + def _updateFirmwareCompletedSucessfully(self): self.setProgress(100, 100) self._firmware_update_finished = True - + self.resetFirmwareUpdate(update_has_finished = True) self.firmwareUpdateComplete.emit() - self.firmwareUpdateChange.emit() + + return ## Upload new firmware to machine # \param filename full path of firmware file to be uploaded @@ -227,8 +271,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice): def firmwareUpdateFinished(self): return self._firmware_update_finished - def resetFirmwareUpdateFinished(self): - self._firmware_update_finished = False + def resetFirmwareUpdate(self, update_has_finished = False): + self._firmware_update_finished = update_has_finished self.firmwareUpdateChange.emit() @pyqtSlot() diff --git a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py index 1a53828d06..801ce4743f 100644 --- a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py +++ b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py @@ -57,6 +57,13 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension): progress += device.progress return progress / len(self._usb_output_devices) + @pyqtProperty(int, notify = progressChanged) + def errorCode(self): + for printer_name, device in self._usb_output_devices.items(): # TODO: @UnusedVariable "printer_name" + if device._error_code: + return device._error_code + return 0 + ## Return True if all printers finished firmware update @pyqtProperty(float, notify = firmwareUpdateChange) def firmwareUpdateCompleteStatus(self): @@ -103,7 +110,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension): return for printer_connection in self._usb_output_devices: - self._usb_output_devices[printer_connection].resetFirmwareUpdateFinished() + self._usb_output_devices[printer_connection].resetFirmwareUpdate() self.spawnFirmwareInterface("") for printer_connection in self._usb_output_devices: try: @@ -111,7 +118,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension): except FileNotFoundError: # Should only happen in dev environments where the resources/firmware folder is absent. self._usb_output_devices[printer_connection].setProgress(100, 100) - Logger.log("w", "No firmware found for printer %s", printer_connection) + Logger.log("w", "No firmware found for printer %s called '%s'" %(printer_connection, self._getDefaultFirmwareName())) Message(i18n_catalog.i18nc("@info", "Could not find firmware required for the printer at %s.") % printer_connection).show() self._firmware_view.close() @@ -126,7 +133,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension): self._usb_output_devices[serial_port].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName())) except FileNotFoundError: self._firmware_view.close() - Logger.log("e", "Could not find firmware required for this machine") + Logger.log("e", "Could not find firmware required for this machine called '%s'" %(self._getDefaultFirmwareName())) return False return True return False @@ -147,12 +154,12 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension): Logger.log("e", "There is no global container stack. Can not update firmware.") self._firmware_view.close() return "" - + # The bottom of the containerstack is the machine definition machine_id = global_container_stack.getBottom().id - + machine_has_heated_bed = global_container_stack.getProperty("machine_heated_bed", "value") - + if platform.system() == "Linux": baudrate = 115200 else: @@ -166,13 +173,15 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension): "bq_hephestos_2" : "MarlinHephestos2.hex", "ultimaker_original" : "MarlinUltimaker-{baudrate}.hex", "ultimaker_original_plus" : "MarlinUltimaker-UMOP-{baudrate}.hex", + "ultimaker_original_dual" : "MarlinUltimaker-{baudrate}-dual.hex", "ultimaker2" : "MarlinUltimaker2.hex", "ultimaker2_go" : "MarlinUltimaker2go.hex", - "ultimaker2_plus" : "MarlinUltimaker2plus.hex", + "ultimaker2_plus" : "MarlinUltimaker2plus.hex", "ultimaker2_extended" : "MarlinUltimaker2extended.hex", "ultimaker2_extended_plus" : "MarlinUltimaker2extended-plus.hex", } machine_with_heated_bed = {"ultimaker_original" : "MarlinUltimaker-HBK-{baudrate}.hex", + "ultimaker_original_dual" : "MarlinUltimaker-HBK-{baudrate}-dual.hex", } ##TODO: Add check for multiple extruders hex_file = None diff --git a/plugins/UltimakerMachineActions/BedLevelMachineAction.py b/plugins/UltimakerMachineActions/BedLevelMachineAction.py index 12df7d6843..c80a8ffc01 100644 --- a/plugins/UltimakerMachineActions/BedLevelMachineAction.py +++ b/plugins/UltimakerMachineActions/BedLevelMachineAction.py @@ -10,7 +10,7 @@ catalog = i18nCatalog("cura") class BedLevelMachineAction(MachineAction): def __init__(self): - super().__init__("BedLevel", catalog.i18nc("@action", "Level bed")) + super().__init__("BedLevel", catalog.i18nc("@action", "Level build plate")) self._qml_url = "BedLevelMachineAction.qml" self._bed_level_position = 0 diff --git a/plugins/UltimakerMachineActions/BedLevelMachineAction.qml b/plugins/UltimakerMachineActions/BedLevelMachineAction.qml index c7c4074120..7f35637516 100644 --- a/plugins/UltimakerMachineActions/BedLevelMachineAction.qml +++ b/plugins/UltimakerMachineActions/BedLevelMachineAction.qml @@ -24,7 +24,7 @@ Cura.MachineAction { id: pageTitle width: parent.width - text: catalog.i18nc("@title", "Bed Leveling") + text: catalog.i18nc("@title", "Build Plate Leveling") wrapMode: Text.WordWrap font.pointSize: 18; } @@ -44,7 +44,7 @@ Cura.MachineAction anchors.topMargin: UM.Theme.getSize("default_margin").height width: parent.width wrapMode: Text.WordWrap - text: catalog.i18nc("@label", "For every position; insert a piece of paper under the nozzle and adjust the print bed height. The print bed height is right when the paper is slightly gripped by the tip of the nozzle.") + text: catalog.i18nc("@label", "For every position; insert a piece of paper under the nozzle and adjust the print build plate height. The print build plate height is right when the paper is slightly gripped by the tip of the nozzle.") } Row @@ -59,7 +59,7 @@ Cura.MachineAction Button { id: startBedLevelingButton - text: catalog.i18nc("@action:button","Start Bed Leveling") + text: catalog.i18nc("@action:button","Start Build Plate Leveling") onClicked: { startBedLevelingButton.visible = false; diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml index 0bdbc27527..6832883c79 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml @@ -220,7 +220,7 @@ Cura.MachineAction anchors.left: parent.left anchors.top: nozzleTempLabel.bottom wrapMode: Text.WordWrap - text: catalog.i18nc("@label","Bed temperature check:") + text: catalog.i18nc("@label","Build plate temperature check:") visible: checkupMachineAction.usbConnected && manager.hasHeatedBed } diff --git a/plugins/UltimakerMachineActions/UMOUpgradeSelection.py b/plugins/UltimakerMachineActions/UMOUpgradeSelection.py index 5d00fd5e3a..64c9ae1180 100644 --- a/plugins/UltimakerMachineActions/UMOUpgradeSelection.py +++ b/plugins/UltimakerMachineActions/UMOUpgradeSelection.py @@ -20,31 +20,25 @@ class UMOUpgradeSelection(MachineAction): @pyqtProperty(bool, notify = heatedBedChanged) def hasHeatedBed(self): global_container_stack = Application.getInstance().getGlobalContainerStack() - return global_container_stack.getProperty("machine_heated_bed", "value") + if global_container_stack: + return global_container_stack.getProperty("machine_heated_bed", "value") - @pyqtSlot() - def addHeatedBed(self): + @pyqtSlot(bool) + def setHeatedBed(self, heated_bed = True): global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack: variant = global_container_stack.findContainer({"type": "variant"}) if variant: if variant.getId() == "empty_variant": variant_index = global_container_stack.getContainerIndex(variant) - stack_name = global_container_stack.getName() - new_variant = UM.Settings.InstanceContainer(stack_name + "_variant") - new_variant.addMetaDataEntry("type", "variant") - new_variant.setDefinition(global_container_stack.getBottom()) - UM.Settings.ContainerRegistry.getInstance().addContainer(new_variant) - global_container_stack.replaceContainer(variant_index, new_variant) - variant = new_variant - variant.setProperty("machine_heated_bed", "value", True) - self.heatedBedChanged.emit() + self._createVariant(global_container_stack, variant_index) + variant.setProperty("machine_heated_bed", "value", heated_bed) + self.heatedBedChanged.emit() - @pyqtSlot() - def removeHeatedBed(self): - global_container_stack = Application.getInstance().getGlobalContainerStack() - if global_container_stack: - variant = global_container_stack.findContainer({"type": "variant"}) - if variant: - variant.setProperty("machine_heated_bed", "value", False) - self.heatedBedChanged.emit() \ No newline at end of file + def _createVariant(self, global_container_stack, variant_index): + # Create and switch to a variant to store the settings in + new_variant = UM.Settings.InstanceContainer(global_container_stack.getName() + "_variant") + new_variant.addMetaDataEntry("type", "variant") + new_variant.setDefinition(global_container_stack.getBottom()) + UM.Settings.ContainerRegistry.getInstance().addContainer(new_variant) + global_container_stack.replaceContainer(variant_index, new_variant) \ No newline at end of file diff --git a/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml b/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml index 234e53c0a6..abb7588a39 100644 --- a/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml +++ b/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml @@ -42,9 +42,9 @@ Cura.MachineAction anchors.top: pageDescription.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height - text: catalog.i18nc("@label", "Heated bed (official kit or self-built)") + text: catalog.i18nc("@label", "Heated Build Plate (official kit or self-built)") checked: manager.hasHeatedBed - onClicked: checked ? manager.addHeatedBed() : manager.removeHeatedBed() + onClicked: manager.setHeatedBed(checked) } UM.I18nCatalog { id: catalog; name: "cura"; } diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 84b78c1d6a..470e65531b 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -21,13 +21,11 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): ## Overridden from InstanceContainer def duplicate(self, new_id, new_name = None): base_file = self.getMetaDataEntry("base_file", None) - new_uuid = str(uuid.uuid4()) if base_file: containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = base_file) if containers: new_basefile = containers[0].duplicate(self.getMetaDataEntry("brand") + "_" + new_id, new_name) - new_basefile.setMetaDataEntry("GUID", new_uuid) base_file = new_basefile.id UM.Settings.ContainerRegistry.getInstance().addContainer(new_basefile) @@ -38,8 +36,8 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): if variant_containers: new_id += "_" + variant_containers[0].getName().replace(" ", "_") + new_id = UM.Settings.ContainerRegistry.getInstance().createUniqueName("material", self._id, new_id, "") result = super().duplicate(new_id, new_name) - result.setMetaDataEntry("GUID", new_uuid) if result.getMetaDataEntry("base_file", None): result.setMetaDataEntry("base_file", base_file) return result @@ -58,13 +56,14 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): super().setMetaDataEntry(key, value) - if key == "material": - self.setName(value) - - for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(GUID = self.getMetaDataEntry("GUID")): + if key == "material" or key == "color_name": + self.setName(self._profile_name(self.getMetaDataEntry("material"), self.getMetaDataEntry("color_name"))) + basefile = self.getMetaDataEntry("base_file", self._id) #if basefile is none, this is a basefile. + # Update all containers that share GUID and basefile + for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(GUID = self.getMetaDataEntry("GUID"), base_file = basefile): container.setMetaData(copy.deepcopy(self._metadata)) - if key == "material": - container.setName(value) + if key == "material" or key == "color_name": + container.setName(self.getName()) ## Overridden from InstanceContainer def setProperty(self, key, property_name, property_value, container = None): @@ -237,7 +236,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): material = entry.find("./um:material", self.__namespaces) color = entry.find("./um:color", self.__namespaces) - self.setName(material.text) + self.setName(self._profile_name(material.text, color.text)) self.addMetaDataEntry("brand", brand.text) self.addMetaDataEntry("material", material.text) @@ -293,7 +292,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): for identifier in identifiers: machine_id = self.__product_id_map.get(identifier.get("product"), None) if machine_id is None: - Logger.log("w", "Cannot create material for unknown machine %s", machine_id) + Logger.log("w", "Cannot create material for unknown machine %s", identifier.get("product")) continue definitions = UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id = machine_id) @@ -369,11 +368,18 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): builder.data(str(instance.value)) builder.end("setting") + def _profile_name(self, material_name, color_name): + if color_name != "Generic": + return "%s %s" % (color_name, material_name) + else: + return material_name + # Map XML file setting names to internal names __material_property_setting_map = { "print temperature": "material_print_temperature", "heated bed temperature": "material_bed_temperature", "standby temperature": "material_standby_temperature", + "processing temperature graph": "material_flow_temp_graph", "print cooling": "cool_fan_speed", "retraction amount": "retraction_amount", "retraction speed": "retraction_speed", @@ -382,11 +388,11 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): # Map XML file product names to internal ids # TODO: Move this to definition's metadata __product_id_map = { - "Ultimaker2": "ultimaker2", - "Ultimaker2+": "ultimaker2_plus", - "Ultimaker2go": "ultimaker2_go", - "Ultimaker2extended": "ultimaker2_extended", - "Ultimaker2extended+": "ultimaker2_extended_plus", + "Ultimaker 2": "ultimaker2", + "Ultimaker 2+": "ultimaker2_plus", + "Ultimaker 2 Go": "ultimaker2_go", + "Ultimaker 2 Extended": "ultimaker2_extended", + "Ultimaker 2 Extended+": "ultimaker2_extended_plus", "Ultimaker Original": "ultimaker_original", "Ultimaker Original+": "ultimaker_original_plus" } diff --git a/resources/definitions/custom.def.json b/resources/definitions/custom.def.json new file mode 100644 index 0000000000..7ae1d1bd28 --- /dev/null +++ b/resources/definitions/custom.def.json @@ -0,0 +1,15 @@ +{ + "id": "custom", + "version": 2, + "name": "Custom FDM printer", + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Ultimaker", + "manufacturer": "Custom", + "category": "Custom", + "file_formats": "text/x-gcode", + "has_materials": true, + "first_start_actions": ["MachineSettingsAction"] + } +} diff --git a/resources/definitions/fdmextruder.def.json b/resources/definitions/fdmextruder.def.json index bde24ee684..16f18d3b2e 100644 --- a/resources/definitions/fdmextruder.def.json +++ b/resources/definitions/fdmextruder.def.json @@ -161,7 +161,7 @@ }, "platform_adhesion": { - "label": "Platform Adhesion", + "label": "Build Plate Adhesion", "type": "category", "icon": "category_adhesion", "description": "Adhesion", @@ -177,8 +177,7 @@ "minimum_value_warning": "machine_nozzle_offset_x", "maximum_value": "machine_width", "settable_per_mesh": false, - "settable_per_extruder": true, - "enabled": false + "settable_per_extruder": true }, "extruder_prime_pos_y": { @@ -190,8 +189,7 @@ "minimum_value_warning": "machine_nozzle_offset_y", "maximum_value_warning": "machine_depth", "settable_per_mesh": false, - "settable_per_extruder": true, - "enabled": false + "settable_per_extruder": true } } } diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index cc15e7c5bb..c1fb8c53e2 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -10,8 +10,8 @@ "manufacturer": "Ultimaker", "file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj;application/x3g", "visible": false, - "preferred_material": "pla", - "preferred_quality": "normal", + "preferred_material": "*generic_pla*", + "preferred_quality": "*normal*", "machine_extruder_trains": { "0": "fdmextruder" @@ -67,8 +67,8 @@ }, "material_bed_temp_wait": { - "label": "Wait for bed heatup", - "description": "Whether to insert a command to wait until the bed temperature is reached at the start.", + "label": "Wait for build plate heatup", + "description": "Whether to insert a command to wait until the build plate temperature is reached at the start.", "default_value": true, "type": "bool", "settable_per_mesh": false, @@ -95,6 +95,16 @@ "settable_per_extruder": false, "settable_per_meshgroup": false }, + "material_bed_temp_prepend": + { + "label": "Include build plate temperature", + "description": "Whether to include build plate temperature commands at the start of the gcode. When the start_gcode already contains build plate temperature commands Cura frontend will automatically disable this setting.", + "default_value": true, + "type": "bool", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, "machine_width": { "label": "Machine width", @@ -127,8 +137,8 @@ }, "machine_heated_bed": { - "label": "Has heated bed", - "description": "Whether the machine has a heated bed present.", + "label": "Has heated build plate", + "description": "Whether the machine has a heated build plate present.", "default_value": false, "type": "bool", "settable_per_mesh": false, @@ -784,7 +794,7 @@ "wall_0_inset": { "label": "Outer Wall Inset", - "description": "Inset applied to the path of the outer wall. If the outer wall is smaller than the nozzle, and printed after the inner walls, use this offset to get the hole in the nozzle to overlap with the inner walls instead of the outside of the object.", + "description": "Inset applied to the path of the outer wall. If the outer wall is smaller than the nozzle, and printed after the inner walls, use this offset to get the hole in the nozzle to overlap with the inner walls instead of the outside of the model.", "unit": "mm", "type": "float", "default_value": 0.0, @@ -1085,8 +1095,8 @@ "settable_per_extruder": true }, "material_bed_temperature": { - "label": "Bed Temperature", - "description": "The temperature used for the heated bed. Set at 0 to pre-heat the printer manually.", + "label": "Build Plate Temperature", + "description": "The temperature used for the heated build plate. Set at 0 to pre-heat the printer manually.", "unit": "°C", "type": "float", "resolve": "sum(extruderValues('material_bed_temperature')) / len(extruderValues('material_bed_temperature'))", @@ -1570,7 +1580,7 @@ "max_feedrate_z_override": { "label": "Maximum Z Speed", - "description": "The maximum speed with which the bed is moved. Setting this to zero causes the print to use the firmware defaults for the maximum z speed.", + "description": "The maximum speed with which the build plate is moved. Setting this to zero causes the print to use the firmware defaults for the maximum z speed.", "unit": "mm/s", "type": "float", "default_value": 0, @@ -1583,7 +1593,7 @@ "speed_slowdown_layers": { "label": "Number of Slower Layers", - "description": "The first few layers are printed slower than the rest of the object, to get better adhesion to the build plate and improve the overall success rate of prints. The speed is gradually increased over these layers.", + "description": "The first few layers are printed slower than the rest of the model, to get better adhesion to the build plate and improve the overall success rate of prints. The speed is gradually increased over these layers.", "type": "int", "default_value": 2, "minimum_value": "0", @@ -2111,8 +2121,8 @@ { "cool_fan_enabled": { - "label": "Enable Cooling Fans", - "description": "Enables the cooling fans while printing. The fans improve print quality on layers with short layer times and bridging / overhangs.", + "label": "Enable Print Cooling", + "description": "Enables the print cooling fans while printing. The fans improve print quality on layers with short layer times and bridging / overhangs.", "type": "bool", "default_value": true, "settable_per_mesh": false, @@ -2121,7 +2131,7 @@ "cool_fan_speed": { "label": "Fan Speed", - "description": "The speed at which the cooling fans spin.", + "description": "The speed at which the print cooling fans spin.", "unit": "%", "type": "float", "minimum_value": "0", @@ -2634,7 +2644,7 @@ }, "platform_adhesion": { - "label": "Platform Adhesion", + "label": "Build Plate Adhesion", "type": "category", "icon": "category_adhesion", "description": "Adhesion", @@ -2668,8 +2678,8 @@ }, "adhesion_type": { - "label": "Platform Adhesion Type", - "description": "Different options that help to improve both priming your extrusion and adhesion to the build plate. Brim adds a single layer flat area around the base of your object to prevent warping. Raft adds a thick grid with a roof below the object. Skirt is a line printed around the object, but not connected to the model.", + "label": "Build Plate Adhesion Type", + "description": "Different options that help to improve both priming your extrusion and adhesion to the build plate. Brim adds a single layer flat area around the base of your model to prevent warping. Raft adds a thick grid with a roof below the model. Skirt is a line printed around the model, but not connected to the model.", "type": "enum", "options": { @@ -2684,7 +2694,7 @@ "skirt_line_count": { "label": "Skirt Line Count", - "description": "Multiple skirt lines help to prime your extrusion better for small objects. Setting this to 0 will disable the skirt.", + "description": "Multiple skirt lines help to prime your extrusion better for small models. Setting this to 0 will disable the skirt.", "type": "int", "default_value": 1, "minimum_value": "0", @@ -2762,7 +2772,7 @@ "raft_margin": { "label": "Raft Extra Margin", - "description": "If the raft is enabled, this is the extra raft area around the object which is also given a raft. Increasing this margin will create a stronger raft while using more material and leaving less area for your print.", + "description": "If the raft is enabled, this is the extra raft area around the model which is also given a raft. Increasing this margin will create a stronger raft while using more material and leaving less area for your print.", "unit": "mm", "type": "float", "default_value": 15, @@ -2773,7 +2783,7 @@ "raft_airgap": { "label": "Raft Air Gap", - "description": "The gap between the final raft layer and the first layer of the object. Only the first layer is raised by this amount to lower the bonding between the raft layer and the object. Makes it easier to peel off the raft.", + "description": "The gap between the final raft layer and the first layer of the model. Only the first layer is raised by this amount to lower the bonding between the raft layer and the model. Makes it easier to peel off the raft.", "unit": "mm", "type": "float", "default_value": 0.3, @@ -2785,7 +2795,7 @@ }, "layer_0_z_overlap": { "label": "Initial Layer Z Overlap", - "description": "Make the first and second layer of the object overlap in the Z direction to compensate for the filament lost in the airgap. All models above the first model layer will be shifted down by this amount.", + "description": "Make the first and second layer of the model overlap in the Z direction to compensate for the filament lost in the airgap. All models above the first model layer will be shifted down by this amount.", "unit": "mm", "type": "float", "default_value": 0.22, @@ -2799,7 +2809,7 @@ "raft_surface_layers": { "label": "Raft Top Layers", - "description": "The number of top layers on top of the 2nd raft layer. These are fully filled layers that the object sits on. 2 layers result in a smoother top surface than 1.", + "description": "The number of top layers on top of the 2nd raft layer. These are fully filled layers that the model sits on. 2 layers result in a smoother top surface than 1.", "type": "int", "default_value": 2, "minimum_value": "0", @@ -2867,7 +2877,7 @@ "raft_interface_line_width": { "label": "Raft Middle Line Width", - "description": "Width of the lines in the middle raft layer. Making the second layer extrude more causes the lines to stick to the bed.", + "description": "Width of the lines in the middle raft layer. Making the second layer extrude more causes the lines to stick to the build plate.", "unit": "mm", "type": "float", "default_value": 0.7, @@ -2895,7 +2905,7 @@ "raft_base_thickness": { "label": "Raft Base Thickness", - "description": "Layer thickness of the base raft layer. This should be a thick layer which sticks firmly to the printer bed.", + "description": "Layer thickness of the base raft layer. This should be a thick layer which sticks firmly to the printer build plate.", "unit": "mm", "type": "float", "default_value": 0.3, @@ -2909,7 +2919,7 @@ "raft_base_line_width": { "label": "Raft Base Line Width", - "description": "Width of the lines in the base raft layer. These should be thick lines to assist in bed adhesion.", + "description": "Width of the lines in the base raft layer. These should be thick lines to assist in build plate adhesion.", "unit": "mm", "type": "float", "default_value": 0.8, @@ -3182,7 +3192,7 @@ { "adhesion_extruder_nr": { - "label": "Platform Adhesion Extruder", + "label": "Build Plate Adhesion Extruder", "description": "The extruder train to use for printing the skirt/brim/raft. This is used in multi-extrusion.", "type": "extruder", "default_value": "0", @@ -3240,6 +3250,7 @@ "label": "Enable Prime Tower", "description": "Print a tower next to the print which serves to prime the material after each nozzle switch.", "type": "bool", + "enabled": "machine_extruder_count > 1", "default_value": false, "settable_per_mesh": false, "settable_per_extruder": false @@ -3311,7 +3322,7 @@ "multiple_mesh_overlap": { "label": "Dual Extrusion Overlap", - "description": "Make the objects printed with different extruder trains overlap a bit. This makes the different materials bond together better.", + "description": "Make the models printed with different extruder trains overlap a bit. This makes the different materials bond together better.", "type": "float", "unit": "mm", "default_value": 0.15, @@ -3322,7 +3333,7 @@ "ooze_shield_enabled": { "label": "Enable Ooze Shield", - "description": "Enable exterior ooze shield. This will create a shell around the object which is likely to wipe a second nozzle if it's at the same height as the first nozzle.", + "description": "Enable exterior ooze shield. This will create a shell around the model which is likely to wipe a second nozzle if it's at the same height as the first nozzle.", "type": "bool", "default_value": false, "settable_per_mesh": false, @@ -3409,7 +3420,7 @@ "print_sequence": { "label": "Print Sequence", - "description": "Whether to print all objects one layer at a time or to wait for one object to finish, before moving on to the next. One at a time mode is only possible if all models are separated in such a way that the whole print head can move in between and all models are lower than the distance between the nozzle and the X/Y axes.", + "description": "Whether to print all models one layer at a time or to wait for one model to finish, before moving on to the next. One at a time mode is only possible if all models are separated in such a way that the whole print head can move in between and all models are lower than the distance between the nozzle and the X/Y axes.", "type": "enum", "options": { @@ -3463,7 +3474,7 @@ "magic_spiralize": { "label": "Spiralize Outer Contour", - "description": "Spiralize smooths out the Z move of the outer edge. This will create a steady Z increase over the whole print. This feature turns a solid object into a single walled print with a solid bottom. This feature used to be called Joris in older versions.", + "description": "Spiralize smooths out the Z move of the outer edge. This will create a steady Z increase over the whole print. This feature turns a solid model into a single walled print with a solid bottom. This feature used to be called Joris in older versions.", "type": "bool", "default_value": false, "settable_per_mesh": true @@ -3481,7 +3492,7 @@ "draft_shield_enabled": { "label": "Enable Draft Shield", - "description": "This will create a wall around the object, which traps (hot) air and shields against exterior airflow. Especially useful for materials which warp easily.", + "description": "This will create a wall around the model, which traps (hot) air and shields against exterior airflow. Especially useful for materials which warp easily.", "type": "bool", "default_value": false, "settable_per_mesh": false, @@ -3503,7 +3514,7 @@ "draft_shield_height_limitation": { "label": "Draft Shield Limitation", - "description": "Set the height of the draft shield. Choose to print the draft shield at the full height of the object or at a limited height.", + "description": "Set the height of the draft shield. Choose to print the draft shield at the full height of the model or at a limited height.", "type": "enum", "options": { @@ -3807,7 +3818,7 @@ "wireframe_printspeed_flat": { "label": "WP Horizontal Printing Speed", - "description": "Speed of printing the horizontal contours of the object. Only applies to Wire Printing.", + "description": "Speed of printing the horizontal contours of the model. Only applies to Wire Printing.", "unit": "mm/s", "type": "float", "default_value": 5, diff --git a/resources/definitions/innovo_inventor.def.json b/resources/definitions/innovo_inventor.def.json index 9ba8d0d44b..511617c3e6 100644 --- a/resources/definitions/innovo_inventor.def.json +++ b/resources/definitions/innovo_inventor.def.json @@ -27,7 +27,7 @@ "default_value": true }, "machine_center_is_zero": { - "default_value": false + "default_value": true }, "machine_nozzle_size": { "default_value": 0.4 @@ -56,7 +56,7 @@ "default_value": "G28 ; Home extruder\nM107 ; Turn off fan\nG90 ; Absolute positioning\nM82 ; Extruder in absolute mode\n{IF_BED}M190 S{BED}\n{IF_EXT0}M104 T0 S{TEMP0}\n{IF_EXT0}M109 T0 S{TEMP0}\n{IF_EXT1}M104 T1 S{TEMP1}\n{IF_EXT1}M109 T1 S{TEMP1}\nG32 S3 ; auto level\nG92 E0 ; Reset extruder position" }, "machine_end_gcode": { - "default_value": "M104 S0\nG91 ; relative positioning\nG1 E-2 F5000; retract 2mm\nG28 Z; move bed down\nG90 ; absolute positioning\nM84 ; disable motors" + "default_value": "M104 S0\nM140 S0 ; heated bed heater off\nG91 ; relative positioning\nG1 E-2 F5000; retract 2mm\nG28 Z; move bed down\nG90 ; absolute positioning\nM84 ; disable motors" }, "layer_height": { "default_value": 0.15 @@ -65,7 +65,7 @@ "default_value": 0.8 }, "top_bottom_thickness": { - "default_value": 0.3 + "default_value": 1.2 }, "material_print_temperature": { "default_value": 215 @@ -96,4 +96,4 @@ "default_value": 10.0 } } -} \ No newline at end of file +} diff --git a/resources/definitions/ultimaker2.def.json b/resources/definitions/ultimaker2.def.json index 9985dea764..81723ae10d 100644 --- a/resources/definitions/ultimaker2.def.json +++ b/resources/definitions/ultimaker2.def.json @@ -18,10 +18,12 @@ }, "overrides": { "machine_start_gcode" : { - "default_value": "" + "default_value": "", + "value": "\"\" if machine_gcode_flavor == \"UltiGCode\" else \"G21 ;metric values\\nG90 ;absolute positioning\\nM82 ;set extruder to absolute mode\\nM107 ;start with the fan off\\nG1 X10 Y0 F4000;move X/Y to min endstops\\nG28 Z0 ;move Z to bottom endstops\\nG1 Z15.0 F9000 ;move the platform to 15mm\\nG92 E0 ;zero the extruded length\\nG1 F200 E10 ;extrude 10 mm of feed stock\\nG92 E0 ;zero the extruded length again\\nG1 F9000\\n;Put printing message on LCD screen\\nM117 Printing...\"" }, "machine_end_gcode" : { - "default_value": "" + "default_value": "", + "value": "\"\" if machine_gcode_flavor == \"UltiGCode\" else \"M104 S0 ;extruder heater off\\nM140 S0 ;heated bed heater off (if you have it)\\nG91 ;relative positioning\\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\\nM84 ;steppers off\\nG90 ;absolute positioning\"" }, "machine_width": { "default_value": 223 diff --git a/resources/definitions/ultimaker2_plus.def.json b/resources/definitions/ultimaker2_plus.def.json index 4c7279b178..9866d65e3d 100644 --- a/resources/definitions/ultimaker2_plus.def.json +++ b/resources/definitions/ultimaker2_plus.def.json @@ -11,9 +11,7 @@ "file_formats": "text/x-gcode", "platform": "ultimaker2_platform.obj", "platform_texture": "Ultimaker2Plusbackplate.png", - "preferred_variant": "ultimaker2_plus_0.4", - "preferred_material": "*pla*", - "preferred_quality": "*normal*", + "preferred_variant": "*0.4*", "has_variants": true, "has_materials": true, "has_machine_materials": true, diff --git a/resources/definitions/ultimaker_original.def.json b/resources/definitions/ultimaker_original.def.json index 3dd4766afa..a4c37e7aa7 100644 --- a/resources/definitions/ultimaker_original.def.json +++ b/resources/definitions/ultimaker_original.def.json @@ -13,8 +13,6 @@ "icon": "icon_ultimaker.png", "platform": "ultimaker_platform.stl", "has_materials": true, - "preferred_material": "*pla*", - "preferred_quality": "*normal*", "first_start_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel"], "supported_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel", "UpgradeFirmware"] }, diff --git a/resources/definitions/ultimaker_original_dual.def.json b/resources/definitions/ultimaker_original_dual.def.json index 04c8aadcc9..6feaa07470 100644 --- a/resources/definitions/ultimaker_original_dual.def.json +++ b/resources/definitions/ultimaker_original_dual.def.json @@ -13,12 +13,10 @@ "icon": "icon_ultimaker.png", "platform": "ultimaker_platform.stl", "has_materials": true, - "preferred_material": "*pla*", - "preferred_quality": "*normal*", "machine_extruder_trains": { - "0": "ultimaker_original_dual_left", - "1": "ultimaker_original_dual_right" + "0": "ultimaker_original_dual_1st", + "1": "ultimaker_original_dual_2nd" }, "first_start_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel"], "supported_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel", "UpgradeFirmware"] @@ -65,12 +63,22 @@ "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 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E6 ;extrude 6 mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." + "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nT1 ;Switch to the 2nd extruder\nG92 E0 ;zero the extruded length\nG1 F200 E6 ;extrude 6 mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F200 E-{switch_extruder_retraction_amount}\nT0 ;Switch to the 1st extruder\nG92 E0 ;zero the extruded length\nG1 F200 E6 ;extrude 6 mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." }, "machine_end_gcode": { - "default_value": "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" + "default_value": "M104 T0 S0 ;1st extruder heater off\nM104 T1 S0 ;2nd 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" }, - "machine_extruder_count": { "default_value": 2 }, - "print_sequence": {"enabled": false} + "machine_extruder_count": { + "default_value": 2 + }, + "print_sequence": { + "enabled": false + }, + "prime_tower_position_x": { + "default_value": 185 + }, + "prime_tower_position_y": { + "default_value": 175 + } } } diff --git a/resources/extruders/ultimaker_original_dual_1st.def.json b/resources/extruders/ultimaker_original_dual_1st.def.json new file mode 100644 index 0000000000..058dbf3028 --- /dev/null +++ b/resources/extruders/ultimaker_original_dual_1st.def.json @@ -0,0 +1,26 @@ +{ + "id": "ultimaker_original_dual_1st", + "version": 2, + "name": "1st Extruder", + "inherits": "fdmextruder", + "metadata": { + "machine": "ultimaker_original_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_extruder_start_pos_abs": { "default_value": true }, + "machine_extruder_start_pos_x": { "value": "prime_tower_position_x" }, + "machine_extruder_start_pos_y": { "value": "prime_tower_position_y" }, + "machine_extruder_end_pos_abs": { "default_value": true }, + "machine_extruder_end_pos_x": { "value": "prime_tower_position_x" }, + "machine_extruder_end_pos_y": { "value": "prime_tower_position_y" } + } +} diff --git a/resources/extruders/ultimaker_original_dual_2nd.def.json b/resources/extruders/ultimaker_original_dual_2nd.def.json new file mode 100644 index 0000000000..4b600d0281 --- /dev/null +++ b/resources/extruders/ultimaker_original_dual_2nd.def.json @@ -0,0 +1,26 @@ +{ + "id": "ultimaker_original_dual_2nd", + "version": 2, + "name": "2nd Extruder", + "inherits": "fdmextruder", + "metadata": { + "machine": "ultimaker_original_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": 21.6 }, + + "machine_extruder_start_pos_abs": { "default_value": true }, + "machine_extruder_start_pos_x": { "value": "prime_tower_position_x" }, + "machine_extruder_start_pos_y": { "value": "prime_tower_position_y" }, + "machine_extruder_end_pos_abs": { "default_value": true }, + "machine_extruder_end_pos_x": { "value": "prime_tower_position_x" }, + "machine_extruder_end_pos_y": { "value": "prime_tower_position_y" } + } +} diff --git a/resources/extruders/ultimaker_original_dual_left.def.json b/resources/extruders/ultimaker_original_dual_left.def.json deleted file mode 100644 index 51268dd8b7..0000000000 --- a/resources/extruders/ultimaker_original_dual_left.def.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "id": "ultimaker_original_dual_left", - "version": 2, - "name": "Left Extruder", - "inherits": "fdmextruder", - "metadata": { - "machine": "ultimaker_original_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 } - } -} diff --git a/resources/extruders/ultimaker_original_dual_right.def.json b/resources/extruders/ultimaker_original_dual_right.def.json deleted file mode 100644 index 9a0cd089fe..0000000000 --- a/resources/extruders/ultimaker_original_dual_right.def.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "id": "ultimaker_original_dual_right", - "version": 2, - "name": "Right Extruder", - "inherits": "fdmextruder", - "metadata": { - "machine": "ultimaker_original_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": 21.6 } - } -} diff --git a/resources/materials/generic_abs.xml.fdm_material b/resources/materials/generic_abs.xml.fdm_material index ceb6e8698e..ef2db0b7fd 100644 --- a/resources/materials/generic_abs.xml.fdm_material +++ b/resources/materials/generic_abs.xml.fdm_material @@ -1,6 +1,6 @@ @@ -10,20 +10,21 @@ Generic PLA profile. Serves as an example file, data in this file is not correct Generic 60636bb4-518f-42e7-8237-fe77b194ebe0 - 0 + 1 #8cb219 - 1.07 + 1.10 2.85 - 250 + 240 80 + 200 - - + + diff --git a/resources/materials/generic_cpe.xml.fdm_material b/resources/materials/generic_cpe.xml.fdm_material index 19a0072de9..1fa878e466 100644 --- a/resources/materials/generic_cpe.xml.fdm_material +++ b/resources/materials/generic_cpe.xml.fdm_material @@ -1,6 +1,6 @@ @@ -10,20 +10,21 @@ Generic PLA profile. Serves as an example file, data in this file is not correct Generic 12f41353-1a33-415e-8b4f-a775a6c70cc6 - 0 + 1 #159499 - 0.94 + 1.27 2.85 250 70 + 175 - - + + diff --git a/resources/materials/generic_pla.xml.fdm_material b/resources/materials/generic_pla.xml.fdm_material index 9d60be93fe..a98e0e469c 100644 --- a/resources/materials/generic_pla.xml.fdm_material +++ b/resources/materials/generic_pla.xml.fdm_material @@ -10,25 +10,42 @@ Generic PLA profile. Serves as an example file, data in this file is not correct Generic 506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9 - 0 + 1 #ffc924 - 1.3 + 1.24 2.85 - 210 + 200 60 + 175 - - + + + + + + + + 150 + + + + + + + + + 150 + diff --git a/resources/meshes/inventor_platform.stl b/resources/meshes/inventor_platform.stl index c83f58b66f..a55ab0705b 100644 Binary files a/resources/meshes/inventor_platform.stl and b/resources/meshes/inventor_platform.stl differ diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 3d08b133cc..9b9e85650f 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -183,7 +183,7 @@ Item Action { id: deleteObjectAction; - text: catalog.i18nc("@action:inmenu","Delete Object"); + text: catalog.i18nc("@action:inmenu","Delete Model"); enabled: UM.Controller.toolsEnabled; iconName: "edit-delete"; } @@ -191,13 +191,13 @@ Item Action { id: centerObjectAction; - text: catalog.i18nc("@action:inmenu","Ce&nter Object on Platform"); + text: catalog.i18nc("@action:inmenu","Ce&nter Model on Platform"); } Action { id: groupObjectsAction - text: catalog.i18nc("@action:inmenu menubar:edit","&Group Objects"); + text: catalog.i18nc("@action:inmenu menubar:edit","&Group Models"); enabled: UM.Scene.numObjectsSelected > 1 ? true: false iconName: "object-group" shortcut: "Ctrl+G"; @@ -207,7 +207,7 @@ Item Action { id: unGroupObjectsAction - text: catalog.i18nc("@action:inmenu menubar:edit","Ungroup Objects"); + text: catalog.i18nc("@action:inmenu menubar:edit","Ungroup Models"); enabled: UM.Scene.isGroupSelected iconName: "object-ungroup" shortcut: "Ctrl+Shift+G"; @@ -217,7 +217,7 @@ Item Action { id: mergeObjectsAction - text: catalog.i18nc("@action:inmenu menubar:edit","&Merge Objects"); + text: catalog.i18nc("@action:inmenu menubar:edit","&Merge Models"); enabled: UM.Scene.numObjectsSelected > 1 ? true: false iconName: "merge"; shortcut: "Ctrl+Alt+G"; @@ -227,14 +227,14 @@ Item Action { id: multiplyObjectAction; - text: catalog.i18nc("@action:inmenu","&Duplicate Object"); + text: catalog.i18nc("@action:inmenu","&Duplicate Model"); iconName: "edit-duplicate" } Action { id: selectAllAction; - text: catalog.i18nc("@action:inmenu menubar:edit","&Select All Objects"); + text: catalog.i18nc("@action:inmenu menubar:edit","&Select All Models"); enabled: UM.Controller.toolsEnabled; iconName: "edit-select-all"; shortcut: "Ctrl+A"; @@ -244,7 +244,7 @@ Item Action { id: deleteAllAction; - text: catalog.i18nc("@action:inmenu menubar:edit","&Clear Build Platform"); + text: catalog.i18nc("@action:inmenu menubar:edit","&Clear Build Plate"); enabled: UM.Controller.toolsEnabled; iconName: "edit-delete"; shortcut: "Ctrl+D"; @@ -254,7 +254,7 @@ Item Action { id: reloadAllAction; - text: catalog.i18nc("@action:inmenu menubar:file","Re&load All Objects"); + text: catalog.i18nc("@action:inmenu menubar:file","Re&load All Models"); iconName: "document-revert"; onTriggered: Printer.reloadAll(); } @@ -262,14 +262,14 @@ Item Action { id: resetAllTranslationAction; - text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Object Positions"); + text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Model Positions"); onTriggered: Printer.resetAllTranslation(); } Action { id: resetAllAction; - text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Object &Transformations"); + text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Model &Transformations"); onTriggered: Printer.resetAll(); } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 02dcd10897..282d14ef06 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -143,20 +143,21 @@ UM.MainWindow model: Cura.ExtrudersModel { } Menu { title: model.name + visible: machineExtruderCount.properties.value > 1 - NozzleMenu { title: catalog.i18nc("@title:menu", "&Nozzle"); visible: Cura.MachineManager.hasVariants } + NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: Cura.MachineManager.hasVariants } MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: Cura.MachineManager.hasMaterials } ProfileMenu { title: catalog.i18nc("@title:menu", "&Profile"); } MenuSeparator { } - MenuItem { text: catalog.i18nc("@action:inmenu", "Set as Active Extruder"); onTriggered: Cura.ExtruderManager.setActiveExtruderIndex(model.index) } + MenuItem { text: catalog.i18nc("@action:inmenu", "Set as Active Extruder"); onTriggered: ExtruderManager.setActiveExtruderIndex(model.index) } } onObjectAdded: settingsMenu.insertItem(index, object) onObjectRemoved: settingsMenu.removeItem(object) } - NozzleMenu { title: catalog.i18nc("@title:menu", "&Nozzle"); visible: machineExtruderCount.properties.value <= 1 && Cura.MachineManager.hasVariants } + NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: machineExtruderCount.properties.value <= 1 && Cura.MachineManager.hasVariants } MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: machineExtruderCount.properties.value <= 1 && Cura.MachineManager.hasMaterials } ProfileMenu { title: catalog.i18nc("@title:menu", "&Profile"); visible: machineExtruderCount.properties.value <= 1 } @@ -459,7 +460,6 @@ UM.MainWindow target: Cura.Actions.addProfile onTriggered: { -// Cura.MachineManager.newQualityContainerFromQualityAndUser(); Cura.ContainerManager.createQualityChanges(); preferences.setPage(4); preferences.show(); @@ -765,10 +765,6 @@ UM.MainWindow addMachineDialog.visible = true addMachineDialog.firstRun = false } - onClearAllFocus: - { - contentItem.focus = true - } } Timer diff --git a/resources/qml/Menus/MaterialMenu.qml b/resources/qml/Menus/MaterialMenu.qml index be2ef4a551..cb90eb721f 100644 --- a/resources/qml/Menus/MaterialMenu.qml +++ b/resources/qml/Menus/MaterialMenu.qml @@ -36,22 +36,7 @@ Menu } MenuItem { - text: - { - var result = model.name - - if(model.metadata.brand != undefined && model.metadata.brand != "Generic") - { - result = model.metadata.brand + " " + result - } - - if(model.metadata.color_name != undefined && model.metadata.color_name != "Generic") - { - result = result + " (%1)".arg(model.metadata.color_name) - } - - return result - } + text: model.name checkable: true; checked: model.id == Cura.MachineManager.activeMaterialId; exclusiveGroup: group; diff --git a/resources/qml/Menus/ProfileMenu.qml b/resources/qml/Menus/ProfileMenu.qml index c99d6a181d..1c9161cd49 100644 --- a/resources/qml/Menus/ProfileMenu.qml +++ b/resources/qml/Menus/ProfileMenu.qml @@ -22,7 +22,7 @@ Menu { text: model.name checkable: true - checked: Cura.MachineManager.activeQualityId == model.id + checked: Cura.MachineManager.activeQualityChangesId == "empty_quality_changes" && Cura.MachineManager.activeQualityType == model.metadata.quality_type exclusiveGroup: group onTriggered: Cura.MachineManager.setActiveQuality(model.id) } @@ -38,7 +38,7 @@ Menu id: customProfileInstantiator model: UM.InstanceContainersModel { - filter: menu.getFilter({ "type": "quality_changes", "extruder": null }); + filter: { "type": "quality_changes", "extruder": null, "definition": Cura.MachineManager.filterQualityByMachine ? Cura.MachineManager.activeDefinitionId : "fdmprinter" }; onModelReset: customSeparator.visible = rowCount() > 0 } @@ -82,7 +82,7 @@ Menu result.definition = Cura.MachineManager.activeDefinitionId; if(Cura.MachineManager.hasMaterials) { - result.material = Cura.MachineManager.activeMaterialId; + result.material = Cura.MachineManager.activeQualityMaterialId; } } else diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index 5fa68b77cc..79168ed6df 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -29,14 +29,14 @@ Rectangle switch(Cura.MachineManager.printerOutputDevices[0].jobState) { case "printing": - case "pre_print": // heating, etc. case "paused": return true; + case "pre_print": // heating, etc. case "wait_cleanup": - case "ready": // nut sure if this occurs, "" seems to be the ready state. case "offline": case "abort": // note sure if this jobState actually occurs in the wild case "error": // after clicking abort you apparently get "error" + case "ready": // ready to print or getting ready case "": // ready to print or getting ready default: return false; @@ -235,12 +235,12 @@ Rectangle text: { var result = ""; - var jobState = Cura.MachineManager.printerOutputDevices[0].jobState; if (!printerConnected) { return ""; } - if (lastJobState !== jobState) { + var jobState = Cura.MachineManager.printerOutputDevices[0].jobState; + if (lastJobState != jobState) { // the userClicked message must disappear when an "automated" jobState comes by userClicked = false; lastJobState = jobState; diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml index 07a188713f..33ce3cc7be 100644 --- a/resources/qml/Preferences/GeneralPage.qml +++ b/resources/qml/Preferences/GeneralPage.qml @@ -46,11 +46,11 @@ UM.PreferencesPage UM.Preferences.resetPreference("view/top_layer_count"); topLayerCountCheckbox.checked = boolCheck(UM.Preferences.getValue("view/top_layer_count")) - if (plugins.model.find("id", "SliceInfoPlugin") > -1) { + if (plugins.find("id", "SliceInfoPlugin") > -1) { UM.Preferences.resetPreference("info/send_slice_info") sendDataCheckbox.checked = boolCheck(UM.Preferences.getValue("info/send_slice_info")) } - if (plugins.model.find("id", "UpdateChecker") > -1) { + if (plugins.find("id", "UpdateChecker") > -1) { UM.Preferences.resetPreference("info/automatic_update_check") checkUpdatesCheckbox.checked = boolCheck(UM.Preferences.getValue("info/automatic_update_check")) } @@ -58,6 +58,9 @@ UM.PreferencesPage Column { + //: Model used to check if a plugin exists + UM.PluginsModel { id: plugins } + //: Language selection label UM.I18nCatalog{id: catalog; name:"cura"} @@ -168,7 +171,7 @@ UM.PreferencesPage UM.TooltipArea { width: childrenRect.width; height: childrenRect.height; - text: catalog.i18nc("@info:tooltip","Moves the camera so the object is in the center of the view when an object is selected") + text: catalog.i18nc("@info:tooltip","Moves the camera so the model is in the center of the view when an model is selected") CheckBox { @@ -182,12 +185,12 @@ UM.PreferencesPage UM.TooltipArea { width: childrenRect.width height: childrenRect.height - text: catalog.i18nc("@info:tooltip", "Should objects on the platform be moved so that they no longer intersect?") + text: catalog.i18nc("@info:tooltip", "Should models on the platform be moved so that they no longer intersect?") CheckBox { id: pushFreeCheckbox - text: catalog.i18nc("@option:check", "Ensure objects are kept apart") + text: catalog.i18nc("@option:check", "Ensure models are kept apart") checked: boolCheck(UM.Preferences.getValue("physics/automatic_push_free")) onCheckedChanged: UM.Preferences.setValue("physics/automatic_push_free", checked) } @@ -247,12 +250,12 @@ UM.PreferencesPage UM.TooltipArea { width: childrenRect.width height: childrenRect.height - text: catalog.i18nc("@info:tooltip","Should objects be scaled to the build volume if they are too large?") + text: catalog.i18nc("@info:tooltip","Should models be scaled to the build volume if they are too large?") CheckBox { id: scaleToFitCheckbox - text: catalog.i18nc("@option:check","Scale large objects") + text: catalog.i18nc("@option:check","Scale large models") checked: boolCheck(UM.Preferences.getValue("mesh/scale_to_fit")) onCheckedChanged: UM.Preferences.setValue("mesh/scale_to_fit", checked) } @@ -261,12 +264,12 @@ UM.PreferencesPage UM.TooltipArea { width: childrenRect.width height: childrenRect.height - text: catalog.i18nc("@info:tooltip","An object may appear extremely small if its unit is for example in meters rather than millimeters. Should these objects be scaled up?") + text: catalog.i18nc("@info:tooltip","An model may appear extremely small if its unit is for example in meters rather than millimeters. Should these models be scaled up?") CheckBox { id: scaleTinyCheckbox - text: catalog.i18nc("@option:check","Scale extremely small objects") + text: catalog.i18nc("@option:check","Scale extremely small models") checked: boolCheck(UM.Preferences.getValue("mesh/scale_tiny_meshes")) onCheckedChanged: UM.Preferences.setValue("mesh/scale_tiny_meshes", checked) } @@ -301,7 +304,7 @@ UM.PreferencesPage } UM.TooltipArea { - visible: plugins.model.find("id", "UpdateChecker") > -1 + visible: plugins.find("id", "UpdateChecker") > -1 width: childrenRect.width height: visible ? childrenRect.height : 0 text: catalog.i18nc("@info:tooltip","Should Cura check for updates when the program is started?") @@ -316,7 +319,7 @@ UM.PreferencesPage } UM.TooltipArea { - visible: plugins.model.find("id", "SliceInfoPlugin") > -1 + visible: plugins.find("id", "SliceInfoPlugin") > -1 width: childrenRect.width height: visible ? childrenRect.height : 0 text: catalog.i18nc("@info:tooltip","Should anonymous data about your print be sent to Ultimaker? Note, no models, IP addresses or other personally identifiable information is sent or stored.") @@ -329,13 +332,5 @@ UM.PreferencesPage onCheckedChanged: UM.Preferences.setValue("info/send_slice_info", checked) } } - - //: Invisible list used to check if a plugin exists - ListView - { - id: plugins - model: UM.PluginsModel { } - visible: false - } } } diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml index ca9184ae6b..3135ab1df6 100644 --- a/resources/qml/Preferences/MachinesPage.qml +++ b/resources/qml/Preferences/MachinesPage.qml @@ -18,7 +18,10 @@ UM.ManagementPage } activeId: Cura.MachineManager.activeMachineId - activeIndex: { + activeIndex: activeMachineIndex() + + function activeMachineIndex() + { for(var i = 0; i < model.rowCount(); i++) { if (model.getItem(i).id == Cura.MachineManager.activeMachineId) { return i; @@ -142,7 +145,16 @@ UM.ManagementPage { id: confirmDialog; object: base.currentItem && base.currentItem.name ? base.currentItem.name : ""; - onYes: Cura.MachineManager.removeMachine(base.currentItem.id); + onYes: + { + Cura.MachineManager.removeMachine(base.currentItem.id); + if(!base.currentItem) + { + objectList.currentIndex = activeMachineIndex() + } + //Force updating currentItem and the details panel + objectList.onCurrentIndexChanged() + } } UM.RenameDialog @@ -152,11 +164,20 @@ UM.ManagementPage onAccepted: { Cura.MachineManager.renameMachine(base.currentItem.id, newName.trim()); - //Reselect current item to update details panel - var index = objectList.currentIndex - objectList.currentIndex = -1 - objectList.currentIndex = index + //Force updating currentItem and the details panel + objectList.onCurrentIndexChanged() } } + + Connections + { + target: Cura.MachineManager + onGlobalContainerChanged: + { + objectList.currentIndex = activeMachineIndex() + objectList.onCurrentIndexChanged() + } + } + } } diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index f4a8df1dcf..4ee8b159ba 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -47,7 +47,17 @@ UM.ManagementPage return -1; } - scrollviewCaption: "Printer: %1, Nozzle: %2".arg(Cura.MachineManager.activeMachineName).arg(Cura.MachineManager.activeVariantName) + scrollviewCaption: + { + if (Cura.MachineManager.hasVariants) + { + catalog.i18nc("@action:label %1 is printer name, %2 is how this printer names variants, %3 is variant name", "Printer: %1, %2: %3").arg(Cura.MachineManager.activeMachineName).arg(Cura.MachineManager.activeDefinitionVariantsName).arg(Cura.MachineManager.activeVariantName) + } + else + { + catalog.i18nc("@action:label %1 is printer name","Printer: %1").arg(Cura.MachineManager.activeMachineName) + } + } detailsVisible: true section.property: "section" @@ -67,8 +77,6 @@ UM.ManagementPage enabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMaterialId onClicked: Cura.MachineManager.setActiveMaterial(base.currentItem.id) }, - /* - // disabled because it has a lot of issues Button { text: catalog.i18nc("@action:button", "Duplicate"); @@ -91,7 +99,7 @@ UM.ManagementPage Cura.MachineManager.setActiveMaterial(material_id) } - }, */ + }, Button { text: catalog.i18nc("@action:button", "Remove"); @@ -188,7 +196,7 @@ UM.ManagementPage object: base.currentItem != null ? base.currentItem.name : "" onYes: { - var containers = Cura.ContainerManager.findInstanceContainers({"GUID": base.currentItem.metadata.GUID}) + var containers = Cura.ContainerManager.findInstanceContainers({"id": base.currentItem.id}) for(var i in containers) { Cura.ContainerManager.removeContainer(containers[i]) diff --git a/resources/qml/Preferences/ProfileTab.qml b/resources/qml/Preferences/ProfileTab.qml new file mode 100644 index 0000000000..90a04ee1e4 --- /dev/null +++ b/resources/qml/Preferences/ProfileTab.qml @@ -0,0 +1,62 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.1 +import QtQuick.Controls 1.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Tab +{ + id: base + + property string extruderId: ""; + property string quality: ""; + property string material: ""; + + TableView + { + anchors.fill: parent + anchors.margins: UM.Theme.getSize("default_margin").width + + TableViewColumn + { + role: "label" + title: catalog.i18nc("@title:column", "Setting") + width: parent.width * 0.4 + } + TableViewColumn + { + role: "profile_value" + title: catalog.i18nc("@title:column", "Profile") + width: parent.width * 0.18 + } + TableViewColumn + { + role: "user_value" + title: catalog.i18nc("@title:column", "Current"); visible: quality == Cura.MachineManager.activeQualityId + width: parent.width * 0.18 + } + TableViewColumn + { + role: "unit" + title: catalog.i18nc("@title:column", "Unit") + width: parent.width * 0.14 + } + + section.property: "category" + section.delegate: Label + { + text: section + font.bold: true + } + + model: Cura.QualitySettingsModel + { + extruderId: base.extruderId != "" ? base.extruderId : "" + quality: base.quality != null ? base.quality : "" + material: base.material != null ? base.material : "" + } + } +} diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index c090a8468d..6eebefa3d5 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -18,13 +18,13 @@ UM.ManagementPage { filter: { - var result = { "type": "quality" }; + var result = { "type": "quality*", "extruder": null }; if(Cura.MachineManager.filterQualityByMachine) { result.definition = Cura.MachineManager.activeDefinitionId; if(Cura.MachineManager.hasMaterials) { - result.material = Cura.MachineManager.activeMaterialId; + result.material = Cura.MachineManager.allActiveMaterialIds[Cura.MachineManager.activeMachineId]; } } else @@ -76,9 +76,9 @@ UM.ManagementPage { var selectedContainer; if (base.currentItem.id == Cura.MachineManager.activeQualityId) { - selectedContainer = Cura.MachineManager.newQualityContainerFromQualityAndUser(); + selectedContainer = Cura.ContainerManager.createQualityChanges(); } else { - selectedContainer = Cura.MachineManager.duplicateContainer(base.currentItem.id); + selectedContainer = Cura.ContainerManager.duplicateQualityOrQualityChanges(base.currentItem.name); } base.selectContainer(selectedContainer); @@ -106,13 +106,15 @@ UM.ManagementPage text: catalog.i18nc("@action:button", "Import"); iconName: "document-import"; onClicked: importDialog.open(); + enabled: false }, Button { text: catalog.i18nc("@action:button", "Export") iconName: "document-export" onClicked: exportDialog.open() - enabled: currentItem != null +// enabled: currentItem != null + enabled: false } ] @@ -152,14 +154,14 @@ UM.ManagementPage return catalog.i18nc("@action:button", "Update profile with current settings"); } enabled: Cura.MachineManager.hasUserSettings && !Cura.MachineManager.isReadOnly(Cura.MachineManager.activeQualityId) - onClicked: Cura.MachineManager.updateQualityContainerFromUserContainer() + onClicked: Cura.ContainerManager.updateQualityChanges() } Button { text: catalog.i18nc("@action:button", "Discard current settings"); enabled: Cura.MachineManager.hasUserSettings - onClicked: Cura.MachineManager.clearUserSettings(); + onClicked: Cura.ContainerManager.clearUserContainers(); } } @@ -173,7 +175,7 @@ UM.ManagementPage Label { id: defaultsMessage - visible: currentItem && !currentItem.metadata.has_settings + visible: false text: catalog.i18nc("@action:label", "This profile has no settings and uses the defaults specified by the printer.") wrapMode: Text.WordWrap width: parent.width @@ -187,69 +189,31 @@ UM.ManagementPage } } - ScrollView { - id: scrollView - + TabView + { anchors.left: parent.left anchors.top: profileNotices.visible ? profileNotices.bottom : profileNotices.anchors.top anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.right: parent.right anchors.bottom: parent.bottom - ListView { - model: Cura.ContainerSettingsModel{ containers: - { - if (!currentItem) { - return [] - } else if (currentItem.id == Cura.MachineManager.activeQualityId) { - return [base.currentItem.id, Cura.MachineManager.activeUserProfileId] - } else { - return [base.currentItem.id] - } - } - } - delegate: Row { - property variant setting: model - spacing: UM.Theme.getSize("default_margin").width/2 - Label { - text: model.label - elide: Text.ElideMiddle - width: scrollView.width / 100 * 40 - } - Repeater { - model: setting.values.length - Label { - text: setting.values[index].toString() - width: scrollView.width / 100 * 15 - elide: Text.ElideRight - font.strikeout: index < setting.values.length - 1 && setting.values[index + 1] != "" - opacity: font.strikeout ? 0.5 : 1 - } - } - Label { - text: model.unit - } - } - header: Row { - visible: currentItem && currentItem.id == Cura.MachineManager.activeQualityId - spacing: UM.Theme.getSize("default_margin").width - Label { - text: catalog.i18nc("@action:label", "Profile:") - width: scrollView.width / 100 * 55 - horizontalAlignment: Text.AlignRight - font.bold: true - } - Label { - text: catalog.i18nc("@action:label", "Current:") - visible: currentItem && currentItem.id == Cura.MachineManager.activeQualityId - font.bold: true - } - } - section.property: "category" - section.criteria: ViewSection.FullString - section.delegate: Label { - text: section - font.bold: true + ProfileTab + { + title: catalog.i18nc("@title:tab", "Global Settings"); + quality: base.currentItem != null ? base.currentItem.id : ""; + material: Cura.MachineManager.allActiveMaterialIds.global ? Cura.MachineManager.allActiveMaterialIds.global : "" + } + + Repeater + { + model: Cura.ExtrudersModel { } + + ProfileTab + { + title: model.name; + extruderId: model.id; + quality: base.currentItem != null ? base.currentItem.id : ""; + material: Cura.MachineManager.allActiveMaterialIds[model.id] } } } @@ -263,17 +227,25 @@ UM.ManagementPage { id: confirmDialog object: base.currentItem != null ? base.currentItem.name : "" - onYes: Cura.MachineManager.removeQualityContainer(base.currentItem.id) + onYes: + { + var name = base.currentItem.name; + Cura.ContainerManager.removeQualityChanges(name) + if(Cura.MachineManager.activeQualityName == name) + { + Cura.MachineManager.setActiveQuality(base.model.getItem(0).name) + } + } } UM.RenameDialog { id: renameDialog; object: base.currentItem != null ? base.currentItem.name : "" property bool removeWhenRejected: false - onAccepted: Cura.MachineManager.renameQualityContainer(base.currentItem.id, newName) + onAccepted: Cura.ContainerManager.renameQualityChanges(base.currentItem.name, newName) onRejected: { if(removeWhenRejected) { - Cura.MachineManager.removeQualityContainer(base.currentItem.id) + Cura.ContainerManager.removeQualityChanges(base.currentItem.name) } } } diff --git a/resources/qml/PrintMonitor.qml b/resources/qml/PrintMonitor.qml index 6460634d8d..c766b2d947 100644 --- a/resources/qml/PrintMonitor.qml +++ b/resources/qml/PrintMonitor.qml @@ -14,6 +14,8 @@ Column id: printMonitor property var connectedPrinter: printerConnected ? Cura.MachineManager.printerOutputDevices[0] : null + Cura.ExtrudersModel { id: extrudersModel } + Label { text: printerConnected ? connectedPrinter.connectionText : catalog.i18nc("@label", "The printer is not connected.") @@ -34,7 +36,7 @@ Column delegate: Loader { sourceComponent: monitorItem - property string label: machineExtruderCount.properties.value > 1 ? catalog.i18nc("@label", "Hotend Temperature %1").arg(index + 1) : catalog.i18nc("@label", "Hotend Temperature") + property string label: machineExtruderCount.properties.value > 1 ? extrudersModel.getItem(index).name : catalog.i18nc("@label", "Hotend") property string value: printerConnected ? Math.round(connectedPrinter.hotendTemperatures[index]) + "°C" : "" } } @@ -44,7 +46,7 @@ Column delegate: Loader { sourceComponent: monitorItem - property string label: catalog.i18nc("@label", "Bed Temperature") + property string label: catalog.i18nc("@label", "Build plate") property string value: printerConnected ? Math.round(connectedPrinter.bedTemperature) + "°C" : "" } } @@ -80,21 +82,24 @@ Column Row { height: UM.Theme.getSize("setting_control").height + width: base.width - 2 * UM.Theme.getSize("default_margin").width Label { + width: parent.width * 0.4 + anchors.verticalCenter: parent.verticalCenter text: label color: printerConnected && printerAcceptsCommands ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text") font: UM.Theme.getFont("default") - width: base.width * 0.4 elide: Text.ElideRight - anchors.verticalCenter: parent.verticalCenter } Label { + width: parent.width * 0.6 + anchors.verticalCenter: parent.verticalCenter text: value color: printerConnected && printerAcceptsCommands ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text") font: UM.Theme.getFont("default") - anchors.verticalCenter: parent.verticalCenter + elide: Text.ElideRight } } } diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 9ea6e181bb..9b63fccf94 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -14,9 +14,7 @@ Rectangle { property real progress: UM.Backend.progress; property int backendState: UM.Backend.state; - property bool activity: Printer.getPlatformActivity; - //Behavior on progress { NumberAnimation { duration: 250; } } property int totalHeight: childrenRect.height + UM.Theme.getSize("default_margin").height property string fileBaseName property string statusText: @@ -26,21 +24,18 @@ Rectangle { return catalog.i18nc("@label:PrintjobStatus", "Please load a 3d model"); } - if(base.backendState == 1) + switch(base.backendState) { - return catalog.i18nc("@label:PrintjobStatus", "Preparing to slice..."); - } - else if(base.backendState == 2) - { - return catalog.i18nc("@label:PrintjobStatus", "Slicing..."); - } - else if(base.backendState == 3) - { - return catalog.i18nc("@label:PrintjobStatus %1 is target operation","Ready to %1").arg(UM.OutputDeviceManager.activeDeviceShortDescription); - } - else if(base.backendState == 4) - { - return catalog.i18nc("@label:PrintjobStatus", "Unable to Slice") + case 1: + return catalog.i18nc("@label:PrintjobStatus", "Preparing to slice..."); + case 2: + return catalog.i18nc("@label:PrintjobStatus", "Slicing..."); + case 3: + return catalog.i18nc("@label:PrintjobStatus %1 is target operation","Ready to %1").arg(UM.OutputDeviceManager.activeDeviceShortDescription); + case 4: + return catalog.i18nc("@label:PrintjobStatus", "Unable to Slice"); + default: + return ""; } } @@ -126,12 +121,29 @@ Rectangle { background: Rectangle { border.width: UM.Theme.getSize("default_lining").width - border.color: !control.enabled ? UM.Theme.getColor("action_button_disabled_border") : - control.pressed ? UM.Theme.getColor("action_button_active_border") : - control.hovered ? UM.Theme.getColor("action_button_hovered_border") : UM.Theme.getColor("action_button_border") - color: !control.enabled ? UM.Theme.getColor("action_button_disabled") : - control.pressed ? UM.Theme.getColor("action_button_active") : - control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") + border.color: + { + if(!control.enabled) + return UM.Theme.getColor("action_button_disabled_border"); + else if(control.pressed) + return UM.Theme.getColor("action_button_active_border"); + else if(control.hovered) + return UM.Theme.getColor("action_button_hovered_border"); + else + return UM.Theme.getColor("action_button_border"); + } + color: + { + if(!control.enabled) + return UM.Theme.getColor("action_button_disabled"); + else if(control.pressed) + return UM.Theme.getColor("action_button_active"); + else if(control.hovered) + return UM.Theme.getColor("action_button_hovered"); + else + return UM.Theme.getColor("action_button"); + } + Behavior on color { ColorAnimation { duration: 50; } } implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("default_margin").width * 2) @@ -139,9 +151,17 @@ Rectangle { Label { id: actualLabel anchors.centerIn: parent - color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") : - control.pressed ? UM.Theme.getColor("action_button_active_text") : - control.hovered ? UM.Theme.getColor("action_button_hovered_text") : UM.Theme.getColor("action_button_text") + color: + { + if(!control.enabled) + return UM.Theme.getColor("action_button_disabled_text"); + else if(control.pressed) + return UM.Theme.getColor("action_button_active_text"); + else if(control.hovered) + return UM.Theme.getColor("action_button_hovered_text"); + else + return UM.Theme.getColor("action_button_text"); + } font: UM.Theme.getFont("action_button") text: control.text; } @@ -167,12 +187,28 @@ Rectangle { background: Rectangle { id: deviceSelectionIcon border.width: UM.Theme.getSize("default_lining").width - border.color: !control.enabled ? UM.Theme.getColor("action_button_disabled_border") : - control.pressed ? UM.Theme.getColor("action_button_active_border") : - control.hovered ? UM.Theme.getColor("action_button_hovered_border") : UM.Theme.getColor("action_button_border") - color: !control.enabled ? UM.Theme.getColor("action_button_disabled") : - control.pressed ? UM.Theme.getColor("action_button_active") : - control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") + border.color: + { + if(!control.enabled) + return UM.Theme.getColor("action_button_disabled_border"); + else if(control.pressed) + return UM.Theme.getColor("action_button_active_border"); + else if(control.hovered) + return UM.Theme.getColor("action_button_hovered_border"); + else + return UM.Theme.getColor("action_button_border"); + } + color: + { + if(!control.enabled) + return UM.Theme.getColor("action_button_disabled"); + else if(control.pressed) + return UM.Theme.getColor("action_button_active"); + else if(control.hovered) + return UM.Theme.getColor("action_button_hovered"); + else + return UM.Theme.getColor("action_button"); + } Behavior on color { ColorAnimation { duration: 50; } } anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("save_button_text_margin").width / 2; @@ -186,9 +222,17 @@ Rectangle { height: UM.Theme.getSize("standard_arrow").height sourceSize.width: width sourceSize.height: height - color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") : - control.pressed ? UM.Theme.getColor("action_button_active_text") : - control.hovered ? UM.Theme.getColor("action_button_hovered_text") : UM.Theme.getColor("action_button_text"); + color: + { + if(!control.enabled) + return UM.Theme.getColor("action_button_disabled_text"); + else if(control.pressed) + return UM.Theme.getColor("action_button_active_text"); + else if(control.hovered) + return UM.Theme.getColor("action_button_hovered_text"); + else + return UM.Theme.getColor("action_button_text"); + } source: UM.Theme.getIcon("arrow_bottom"); } } diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index b88c12ad36..a3b5cbc6d0 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -95,26 +95,31 @@ ScrollView { target: provider property: "containerStackId" - when: model.settable_per_extruder || (inheritStackProvider.properties.global_inherits_stack != -1 && inheritStackProvider.properties.global_inherits_stack != null) + when: model.settable_per_extruder || model.settable_per_mesh || (inheritStackProvider.properties.global_inherits_stack != null && inheritStackProvider.properties.global_inherits_stack >= 0); value: { - if(inheritStackProvider.properties.global_inherits_stack == -1 || inheritStackProvider.properties.global_inherits_stack == null) + if(!model.settable_per_extruder && !model.settable_per_mesh) { - if( ExtruderManager.activeExtruderStackId) - { - return ExtruderManager.activeExtruderStackId - } - else - { - return Cura.MachineManager.activeMachineId - } + //Not settable per extruder, so we must pick global. + return Cura.MachineManager.activeMachineId; } - return ExtruderManager.extruderIds[String(inheritStackProvider.properties.global_inherits_stack)] + if(inheritStackProvider.properties.global_inherits_stack != null && inheritStackProvider.properties.global_inherits_stack >= 0) + { + //We have global_inherits_stack, so pick that stack. + return ExtruderManager.extruderIds[String(inheritStackProvider.properties.global_inherits_stack)]; + } + if(ExtruderManager.activeExtruderStackId) + { + //We're on an extruder tab. Pick the current extruder. + return ExtruderManager.activeExtruderStackId; + } + //No extruder tab is selected. Pick the global stack. Shouldn't happen any more since we removed the global tab. + return Cura.MachineManager.activeMachineId; } } // Specialty provider that only watches global_inherits (we cant filter on what property changed we get events - // so we bypass that to make a dedicated provider. + // so we bypass that to make a dedicated provider). UM.SettingPropertyProvider { id: inheritStackProvider diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index b1cf45f6d3..ff2a6a19a2 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -183,8 +183,23 @@ Column Label { id: variantLabel - text: (Cura.MachineManager.hasVariants && Cura.MachineManager.hasMaterials) ? catalog.i18nc("@label","Nozzle & Material:"): - Cura.MachineManager.hasVariants ? catalog.i18nc("@label","Nozzle:") : catalog.i18nc("@label","Material:"); + text: + { + var label; + if(Cura.MachineManager.hasVariants && Cura.MachineManager.hasMaterials) + { + label = "%1 & %2".arg(Cura.MachineManager.activeDefinitionVariantsName).arg(catalog.i18nc("@label","Material")); + } + else if(Cura.MachineManager.hasVariants) + { + label = Cura.MachineManager.activeDefinitionVariantsName; + } + else + { + label = catalog.i18nc("@label","Material"); + } + return "%1:".arg(label); + } anchors.verticalCenter: parent.verticalCenter width: parent.width * 0.45 - UM.Theme.getSize("default_margin").width diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index ac9642cc06..b60a593629 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -213,42 +213,54 @@ Item id: adhesionHelperLabel anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_margin").width - anchors.verticalCenter: brimCheckBox.verticalCenter + anchors.verticalCenter: adhesionCheckBox.verticalCenter width: parent.width / 100 * 35 - 3 * UM.Theme.getSize("default_margin").width //: Bed adhesion label - text: catalog.i18nc("@label:listbox", "Bed Adhesion:"); + text: catalog.i18nc("@label", "Helper Parts:"); font: UM.Theme.getFont("default"); color: UM.Theme.getColor("text"); } CheckBox{ - id: brimCheckBox - property alias _hovered: brimMouseArea.containsMouse + id: adhesionCheckBox + property alias _hovered: adhesionMouseArea.containsMouse anchors.top: parent.top anchors.left: adhesionHelperLabel.right anchors.leftMargin: UM.Theme.getSize("default_margin").width - //: Setting enable skirt adhesion checkbox - text: catalog.i18nc("@option:check", "Print Brim"); + //: Setting enable printing build-plate adhesion helper checkbox + text: catalog.i18nc("@option:check", "Print Build Plate Adhesion"); style: UM.Theme.styles.checkbox; enabled: base.settingsEnabled - checked: platformAdhesionType.properties.value == "brim" + checked: platformAdhesionType.properties.value != "skirt" MouseArea { - id: brimMouseArea + id: adhesionMouseArea anchors.fill: parent hoverEnabled: true enabled: base.settingsEnabled onClicked: { - platformAdhesionType.setPropertyValue("value", !parent.checked ? "brim" : "skirt") + var adhesionType = "skirt"; + if(!parent.checked) + { + // Remove the "user" setting to see if the rest of the stack prescribes a brim or a raft + platformAdhesionType.removeFromContainer(0); + adhesionType = platformAdhesionType.properties.value; + if(adhesionType == "skirt") + { + // If the rest of the stack doesn't prescribe an adhesion-type, default to a brim + adhesionType = "brim"; + } + } + platformAdhesionType.setPropertyValue("value", adhesionType); } onEntered: { - base.showTooltip(brimCheckBox, Qt.point(-brimCheckBox.x, 0), - catalog.i18nc("@label", "Enable printing a brim. This will add a single-layer-thick flat area around your object which is easy to cut off afterwards.")); + base.showTooltip(adhesionCheckBox, Qt.point(-adhesionCheckBox.x, 0), + catalog.i18nc("@label", "Enable printing a brim or raft. This will add a flat area around or under your object which is easy to cut off afterwards.")); } onExited: { @@ -264,7 +276,7 @@ Item anchors.verticalCenter: supportCheckBox.verticalCenter width: parent.width / 100 * 35 - 3 * UM.Theme.getSize("default_margin").width //: Support label - text: catalog.i18nc("@label:listbox", "Support:"); + text: ""; font: UM.Theme.getFont("default"); color: UM.Theme.getColor("text"); } @@ -274,7 +286,7 @@ Item visible: machineExtruderCount.properties.value <= 1 property alias _hovered: supportMouseArea.containsMouse - anchors.top: brimCheckBox.bottom + anchors.top: adhesionCheckBox.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.left: supportHelperLabel.right anchors.leftMargin: UM.Theme.getSize("default_margin").width @@ -311,7 +323,7 @@ Item visible: machineExtruderCount.properties.value > 1 model: extruderModel - anchors.top: brimCheckBox.bottom + anchors.top: adhesionCheckBox.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.left: supportHelperLabel.right anchors.leftMargin: UM.Theme.getSize("default_margin").width @@ -354,12 +366,11 @@ Item Component.onCompleted: populateExtruderModel() } - //: Invisible list used to populate the extrudelModel - ListView + //: Model used to populate the extrudelModel + Cura.ExtrudersModel { id: extruders - model: Cura.ExtrudersModel { onModelChanged: populateExtruderModel() } - visible: false + onModelChanged: populateExtruderModel() } } @@ -370,10 +381,10 @@ Item text: catalog.i18nc("@label", "Don't print support"), color: "" }) - for(var extruderNumber = 0; extruderNumber < extruders.model.rowCount() ; extruderNumber++) { + for(var extruderNumber = 0; extruderNumber < extruders.rowCount() ; extruderNumber++) { extruderModel.append({ - text: catalog.i18nc("@label", "Print using %1").arg(extruders.model.getItem(extruderNumber).name), - color: extruders.model.getItem(extruderNumber).color + text: catalog.i18nc("@label", "Print support using %1").arg(extruders.getItem(extruderNumber).name), + color: extruders.getItem(extruderNumber).color }) } } diff --git a/resources/quality/high.inst.cfg b/resources/quality/high.inst.cfg index b4498c6c8b..ec3ab18fd6 100644 --- a/resources/quality/high.inst.cfg +++ b/resources/quality/high.inst.cfg @@ -5,6 +5,7 @@ definition = fdmprinter [metadata] type = quality +quality_type = high weight = -3 [values] diff --git a/resources/quality/low.inst.cfg b/resources/quality/low.inst.cfg index d34a7c6461..787325c27c 100644 --- a/resources/quality/low.inst.cfg +++ b/resources/quality/low.inst.cfg @@ -5,6 +5,7 @@ definition = fdmprinter [metadata] type = quality +quality_type = low weight = -1 [values] diff --git a/resources/quality/normal.inst.cfg b/resources/quality/normal.inst.cfg index 417c7c700f..cfd70de49c 100644 --- a/resources/quality/normal.inst.cfg +++ b/resources/quality/normal.inst.cfg @@ -5,6 +5,7 @@ definition = fdmprinter [metadata] type = quality +quality_type = normal weight = -2 [values] diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.25_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.25_normal.inst.cfg index 11b926378d..2d0a81c8a4 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.25_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus type = quality material = generic_abs_ultimaker2_extended_plus_0.25_mm weight = -2 +quality_type = high [values] layer_height = 0.06 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_fast.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_fast.inst.cfg index bdeeb935f4..a882429f2a 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_fast.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus type = quality material = generic_abs_ultimaker2_extended_plus_0.4_mm weight = -1 +quality_type = fast [values] layer_height = 0.15 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_high.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_high.inst.cfg index d658ee5bb5..5650353f7a 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_high.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_high.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus type = quality material = generic_abs_ultimaker2_extended_plus_0.4_mm weight = -3 +quality_type = high [values] layer_height = 0.06 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_normal.inst.cfg index ff024ccc69..6b5901ad15 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.4_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus type = quality material = generic_abs_ultimaker2_extended_plus_0.4_mm weight = -2 +quality_type = normal [values] layer_height = 0.1 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.6_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.6_normal.inst.cfg index c2f4daa86f..bcc2f29656 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.6_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus type = quality material = generic_abs_ultimaker2_extended_plus_0.6_mm weight = -2 +quality_type = normal [values] layer_height = 0.15 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.8_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.8_normal.inst.cfg index 362e844214..3fea836f7a 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_abs_0.8_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus type = quality material = generic_abs_ultimaker2_extended_plus_0.8_mm weight = -2 +quality_type = fast [values] layer_height = 0.2 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.25_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.25_normal.inst.cfg index f4d9264d3e..7774ba3534 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.25_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus type = quality material = generic_cpe_ultimaker2_extended_plus_0.25_mm weight = -2 +quality_type = high [values] layer_height = 0.06 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_fast.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_fast.inst.cfg index f514f22294..256c304149 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_fast.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus type = quality material = generic_cpe_ultimaker2_extended_plus_0.4_mm weight = -1 +quality_type = fast [values] layer_height = 0.15 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_high.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_high.inst.cfg index 0c68be2f5f..0c70fe615d 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_high.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_high.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus type = quality material = generic_cpe_ultimaker2_extended_plus_0.4_mm weight = -3 +quality_type = high [values] layer_height = 0.06 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_normal.inst.cfg index 26ea8ce9bc..541ea1b468 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.4_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus type = quality material = generic_cpe_ultimaker2_extended_plus_0.4_mm weight = -2 +quality_type = normal [values] layer_height = 0.1 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.6_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.6_normal.inst.cfg index d6d10dbe1a..09e9ec2941 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.6_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus type = quality material = generic_cpe_ultimaker2_extended_plus_0.6_mm weight = -2 +quality_type = normal [values] layer_height = 0.15 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.8_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.8_normal.inst.cfg index 53d5e0bc8c..e3d8d5462f 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_cpe_0.8_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus type = quality material = generic_cpe_ultimaker2_extended_plus_0.8_mm weight = -2 +quality_type = fast [values] layer_height = 0.2 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.25_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.25_normal.inst.cfg index 5e54b3194a..346df10c44 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.25_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus type = quality material = generic_pla_ultimaker2_extended_plus_0.25_mm weight = -2 +quality_type = high [values] layer_height = 0.06 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_fast.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_fast.inst.cfg index 893256bb33..7bba3a504d 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_fast.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus type = quality material = generic_pla_ultimaker2_extended_plus_0.4_mm weight = -1 +quality_type = fast [values] layer_height = 0.15 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_high.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_high.inst.cfg index 844e2b3eac..939d099d55 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_high.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_high.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus type = quality material = generic_pla_ultimaker2_extended_plus_0.4_mm weight = -3 +quality_type = high [values] layer_height = 0.06 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_normal.inst.cfg index 47d511446a..7e521e8fe8 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.4_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus type = quality material = generic_pla_ultimaker2_extended_plus_0.4_mm weight = -2 +quality_type = normal [values] layer_height = 0.1 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.6_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.6_normal.inst.cfg index a2b15b6f16..0745ed891f 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.6_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus material = generic_pla_ultimaker2_extended_plus_0.6_mm type = quality weight = -2 +quality_type = normal [values] layer_height = 0.15 diff --git a/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.8_normal.inst.cfg b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.8_normal.inst.cfg index 08b5bec667..f5f66dce6c 100644 --- a/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_extended_plus/um2ep_pla_0.8_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_extended_plus material = generic_pla_ultimaker2_extended_plus_0.8_mm type = quality weight = -2 +quality_type = fast [values] layer_height = 0.2 diff --git a/resources/quality/ultimaker2_plus/pla_0.25_normal.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.25_normal.inst.cfg index 9a44582610..18d74386cf 100644 --- a/resources/quality/ultimaker2_plus/pla_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.25_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus type = quality material = generic_pla_ultimaker2_plus_0.25_mm weight = -2 +quality_type = high [values] layer_height = 0.06 diff --git a/resources/quality/ultimaker2_plus/pla_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.4_fast.inst.cfg index 0df882e418..b17a1f2a6a 100644 --- a/resources/quality/ultimaker2_plus/pla_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.4_fast.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus type = quality material = generic_pla_ultimaker2_plus_0.4_mm weight = -1 +quality_type = fast [values] layer_height = 0.15 diff --git a/resources/quality/ultimaker2_plus/pla_0.4_high.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.4_high.inst.cfg index a8abdb081f..ff542c7c19 100644 --- a/resources/quality/ultimaker2_plus/pla_0.4_high.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.4_high.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus type = quality material = generic_pla_ultimaker2_plus_0.4_mm weight = -3 +quality_type = high [values] layer_height = 0.06 diff --git a/resources/quality/ultimaker2_plus/pla_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.4_normal.inst.cfg index c29b017bbe..79d868f25f 100644 --- a/resources/quality/ultimaker2_plus/pla_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.4_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus type = quality material = generic_pla_ultimaker2_plus_0.4_mm weight = -2 +quality_type = normal [values] layer_height = 0.1 diff --git a/resources/quality/ultimaker2_plus/pla_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.6_normal.inst.cfg index 5a0a840ae7..63beca8fbb 100644 --- a/resources/quality/ultimaker2_plus/pla_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.6_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus material = generic_pla_ultimaker2_plus_0.6_mm type = quality weight = -2 +quality_type = normal [values] layer_height = 0.15 diff --git a/resources/quality/ultimaker2_plus/pla_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.8_normal.inst.cfg index bd90b8c059..f2b78846a8 100644 --- a/resources/quality/ultimaker2_plus/pla_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.8_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus material = generic_pla_ultimaker2_plus_0.8_mm type = quality weight = -2 +quality_type = fast [values] layer_height = 0.2 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.25_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.25_normal.inst.cfg index 05a9bce71c..8457d5cd2b 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.25_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus type = quality material = generic_abs_ultimaker2_plus_0.25_mm weight = -2 +quality_type = high [values] layer_height = 0.06 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.4_fast.inst.cfg index cd0a25981a..8bcb3efee4 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.4_fast.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus type = quality material = generic_abs_ultimaker2_plus_0.4_mm weight = -1 +quality_type = fast [values] layer_height = 0.15 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.4_high.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.4_high.inst.cfg index 4b1ece31ef..f2235abd41 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.4_high.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.4_high.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus type = quality material = generic_abs_ultimaker2_plus_0.4_mm weight = -3 +quality_type = high [values] layer_height = 0.06 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.4_normal.inst.cfg index f1b01fd408..1b8c1035db 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.4_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus type = quality material = generic_abs_ultimaker2_plus_0.4_mm weight = -2 +quality_type = normal [values] layer_height = 0.1 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.6_normal.inst.cfg index 89e73dda38..4ef0f34484 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.6_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus type = quality material = generic_abs_ultimaker2_plus_0.6_mm weight = -2 +quality_type = normal [values] layer_height = 0.15 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.8_normal.inst.cfg index 2171fd3837..ca2f736c01 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.8_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus type = quality material = generic_abs_ultimaker2_plus_0.8_mm weight = -2 +quality_type = fast [values] layer_height = 0.2 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg index 6a300ba27d..0f2a612619 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus type = quality material = generic_cpe_ultimaker2_plus_0.25_mm weight = -2 +quality_type = high [values] layer_height = 0.06 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_fast.inst.cfg index e76c5205f5..8f8fb9e01b 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_fast.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus type = quality material = generic_cpe_ultimaker2_plus_0.4_mm weight = -1 +quality_type = fast [values] layer_height = 0.15 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_high.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_high.inst.cfg index 60f171dc17..abc5e562f7 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_high.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_high.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus type = quality material = generic_cpe_ultimaker2_plus_0.4_mm weight = -3 +quality_type = high [values] layer_height = 0.06 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_normal.inst.cfg index 04116dbe21..5531f245f0 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus type = quality material = generic_cpe_ultimaker2_plus_0.4_mm weight = -2 +quality_type = normal [values] layer_height = 0.1 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.6_normal.inst.cfg index 35e666e6d9..3765f98709 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.6_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus type = quality material = generic_cpe_ultimaker2_plus_0.6_mm weight = -2 +quality_type = normal [values] layer_height = 0.15 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.8_normal.inst.cfg index c36d1714a0..179b554973 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.8_normal.inst.cfg @@ -7,6 +7,7 @@ definition = ultimaker2_plus type = quality material = generic_cpe_ultimaker2_plus_0.8_mm weight = -2 +quality_type = fast [values] layer_height = 0.2