diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 1709a67dd5..03ae28e275 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -191,20 +191,22 @@ class BuildVolume(SceneNode): else: self._disallowed_area_mesh = None - self._volume_aabb = AxisAlignedBox(minimum = Vector(min_w, min_h - 1.0, min_d), maximum = Vector(max_w, max_h, max_d)) + self._volume_aabb = AxisAlignedBox( + minimum = Vector(min_w, min_h - 1.0, min_d), + maximum = Vector(max_w, max_h - self._raft_thickness, max_d)) - skirt_size = 0.0 + bed_adhesion_size = 0.0 container_stack = Application.getInstance().getGlobalContainerStack() if container_stack: - skirt_size = self._getSkirtSize(container_stack) + bed_adhesion_size = self._getBedAdhesionSize(container_stack) # As this works better for UM machines, we only add the disallowed_area_size for the z direction. # This is probably wrong in all other cases. TODO! # The +1 and -1 is added as there is always a bit of extra room required to work properly. scale_to_max_bounds = AxisAlignedBox( - minimum = Vector(min_w + skirt_size + 1, min_h, min_d + disallowed_area_size - skirt_size + 1), - maximum = Vector(max_w - skirt_size - 1, max_h, max_d - disallowed_area_size + skirt_size - 1) + minimum = Vector(min_w + bed_adhesion_size + 1, min_h, min_d + disallowed_area_size - bed_adhesion_size + 1), + maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness, max_d - disallowed_area_size + bed_adhesion_size - 1) ) Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds @@ -309,62 +311,62 @@ class BuildVolume(SceneNode): [prime_x - PRIME_CLEARANCE, prime_y + PRIME_CLEARANCE], ]) - skirt_size = self._getSkirtSize(self._active_container_stack) + bed_adhesion_size = self._getBedAdhesionSize(self._active_container_stack) if disallowed_areas: # Extend every area already in the disallowed_areas with the skirt size. for area in disallowed_areas: poly = Polygon(numpy.array(area, numpy.float32)) - poly = poly.getMinkowskiHull(Polygon(approximatedCircleVertices(skirt_size))) + poly = poly.getMinkowskiHull(Polygon(approximatedCircleVertices(bed_adhesion_size))) areas.append(poly) # Add the skirt areas around the borders of the build plate. - if skirt_size > 0: + 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 areas.append(Polygon(numpy.array([ [-half_machine_width, -half_machine_depth], [-half_machine_width, half_machine_depth], - [-half_machine_width + skirt_size, half_machine_depth - skirt_size], - [-half_machine_width + skirt_size, -half_machine_depth + skirt_size] + [-half_machine_width + bed_adhesion_size, half_machine_depth - bed_adhesion_size], + [-half_machine_width + bed_adhesion_size, -half_machine_depth + bed_adhesion_size] ], numpy.float32))) areas.append(Polygon(numpy.array([ [half_machine_width, half_machine_depth], [half_machine_width, -half_machine_depth], - [half_machine_width - skirt_size, -half_machine_depth + skirt_size], - [half_machine_width - skirt_size, half_machine_depth - skirt_size] + [half_machine_width - bed_adhesion_size, -half_machine_depth + bed_adhesion_size], + [half_machine_width - bed_adhesion_size, half_machine_depth - bed_adhesion_size] ], numpy.float32))) areas.append(Polygon(numpy.array([ [-half_machine_width, half_machine_depth], [half_machine_width, half_machine_depth], - [half_machine_width - skirt_size, half_machine_depth - skirt_size], - [-half_machine_width + skirt_size, half_machine_depth - skirt_size] + [half_machine_width - bed_adhesion_size, half_machine_depth - bed_adhesion_size], + [-half_machine_width + bed_adhesion_size, half_machine_depth - bed_adhesion_size] ], numpy.float32))) areas.append(Polygon(numpy.array([ [half_machine_width, -half_machine_depth], [-half_machine_width, -half_machine_depth], - [-half_machine_width + skirt_size, -half_machine_depth + skirt_size], - [half_machine_width - skirt_size, -half_machine_depth + skirt_size] + [-half_machine_width + bed_adhesion_size, -half_machine_depth + bed_adhesion_size], + [half_machine_width - bed_adhesion_size, -half_machine_depth + bed_adhesion_size] ], numpy.float32))) self._disallowed_areas = areas ## Convenience function to calculate the size of the bed adhesion in directions x, y. - def _getSkirtSize(self, container_stack): + def _getBedAdhesionSize(self, container_stack): skirt_size = 0.0 adhesion_type = container_stack.getProperty("adhesion_type", "value") if adhesion_type == "skirt": skirt_distance = container_stack.getProperty("skirt_gap", "value") skirt_line_count = container_stack.getProperty("skirt_line_count", "value") - skirt_size = skirt_distance + (skirt_line_count * container_stack.getProperty("skirt_line_width", "value")) + skirt_size = skirt_distance + (skirt_line_count * container_stack.getProperty("skirt_brim_line_width", "value")) elif adhesion_type == "brim": - skirt_size = container_stack.getProperty("brim_line_count", "value") * container_stack.getProperty("skirt_line_width", "value") + skirt_size = container_stack.getProperty("brim_line_count", "value") * container_stack.getProperty("skirt_brim_line_width", "value") elif adhesion_type == "raft": skirt_size = container_stack.getProperty("raft_margin", "value") @@ -379,5 +381,5 @@ class BuildVolume(SceneNode): def _clamp(self, value, min_value, max_value): return max(min(value, max_value), min_value) - _skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_line_width", "brim_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist", "xy_offset"] + _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"] diff --git a/cura/ConvexHullDecorator.py b/cura/ConvexHullDecorator.py index f8d1a1e12e..3ccb9481f9 100644 --- a/cura/ConvexHullDecorator.py +++ b/cura/ConvexHullDecorator.py @@ -157,7 +157,7 @@ class ConvexHullDecorator(SceneNodeDecorator): vertex_data = mesh.getConvexHullTransformedVertices(world_transform) # Don't use data below 0. # TODO; We need a better check for this as this gives poor results for meshes with long edges. - vertex_data = vertex_data[vertex_data[:,1] >= 0] + vertex_data = vertex_data[vertex_data[:,1] >= -0.01] if len(vertex_data) >= 4: # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 32a7391159..a6d0a3b827 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -97,6 +97,7 @@ class CuraApplication(QtApplication): SettingDefinition.addSupportedProperty("settable_per_extruder", DefinitionPropertyType.Any, default = True) SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True) SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True) + SettingDefinition.addSupportedProperty("global_inherits_stack", DefinitionPropertyType.Function, default = "-1") SettingDefinition.addSettingType("extruder", int, str, Validator) ## Add the 4 types of profiles to storage. @@ -582,7 +583,8 @@ class CuraApplication(QtApplication): op.push() if group_node: - if len(group_node.getChildren()) == 1: + if len(group_node.getChildren()) == 1 and group_node.callDecoration("isGroup"): + group_node.getChildren()[0].translate(group_node.getPosition()) group_node.getChildren()[0].setParent(group_node.getParent()) op = RemoveSceneNodeOperation(group_node) op.push() @@ -851,7 +853,11 @@ class CuraApplication(QtApplication): def _reloadMeshFinished(self, job): # TODO; This needs to be fixed properly. We now make the assumption that we only load a single mesh! - job._node.setMeshData(job.getResult().getMeshData()) + mesh_data = job.getResult().getMeshData() + if mesh_data: + job._node.setMeshData(mesh_data) + else: + Logger.log("w", "Could not find a mesh in reloaded node.") def _openFile(self, file): job = ReadMeshJob(os.path.abspath(file)) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 191f7b0e27..d2a848dd72 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -48,7 +48,14 @@ class PlatformPhysics: bbox = node.getBoundingBox() # Ignore intersections with the bottom - build_volume_bounding_box = self._build_volume.getBoundingBox().set(bottom=-9001) + build_volume_bounding_box = self._build_volume.getBoundingBox() + if build_volume_bounding_box: + # It's over 9000! + build_volume_bounding_box = build_volume_bounding_box.set(bottom=-9001) + else: + # No bounding box. This is triggered when running Cura from command line with a model for the first time + # In that situation there is a model, but no machine (and therefore no build volume. + return node._outside_buildarea = False # Mark the node as outside the build volume if the bounding box test fails. diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 4dedf9db16..096170ba22 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -95,7 +95,7 @@ class PrintInformation(QObject): else: # Machine with no extruder stacks density = Application.getInstance().getGlobalContainerStack().getMetaDataEntry("properties", {}).get("density", 0) - self._material_weights.append(float(amount) * float(density)) + self._material_weights.append(float(amount) * float(density) / 1000) self._material_lengths.append(round((amount / (math.pi * r ** 2)) / 1000, 2)) self.materialLengthsChanged.emit() self.materialWeightsChanged.emit() diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 744a6811c5..47359a5e97 100644 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -22,7 +22,7 @@ class ExtruderManager(QObject): def __init__(self, parent = None): super().__init__(parent) self._extruder_trains = { } #Per machine, a dictionary of extruder container stack IDs. - self._active_extruder_index = -1 + self._active_extruder_index = 0 UM.Application.getInstance().globalContainerStackChanged.connect(self.__globalContainerStackChanged) self._addCurrentMachineExtruders() @@ -41,6 +41,19 @@ class ExtruderManager(QObject): except KeyError: # Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong. return None + @pyqtProperty(int, notify = extrudersChanged) + def extruderCount(self): + if not UM.Application.getInstance().getGlobalContainerStack(): + return 0 # No active machine, so no extruders. + return len(self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()]) + + @pyqtProperty("QVariantMap", notify=extrudersChanged) + def extruderIds(self): + map = {} + for position in self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()]: + map[position] = self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()][position].getId() + return map + ## The instance of the singleton pattern. # # It's None if the extruder manager hasn't been created yet. @@ -106,8 +119,11 @@ class ExtruderManager(QObject): for extruder_train in extruder_trains: self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train - # Ensure that the extruder train stacks are linked to global stack. - extruder_train.setNextStack(UM.Application.getInstance().getGlobalContainerStack()) + # 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) changed = True if changed: self.extrudersChanged.emit(machine_id) @@ -220,7 +236,11 @@ class ExtruderManager(QObject): container_registry.addContainer(user_profile) container_stack.addContainer(user_profile) - container_stack.setNextStack(UM.Application.getInstance().getGlobalContainerStack()) + # 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) container_registry.addContainer(container_stack) diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index 15e80d3f6b..cbe127d511 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -120,7 +120,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): material = extruder.findContainer({ "type": "material" }) if material: extruder_name = "%s (%s)" % (material.getName(), extruder_name) - position = extruder.getBottom().getMetaDataEntry("position", default = "0") #Position in the definition. + position = extruder.getMetaDataEntry("position", default = "0") # Get the position try: position = int(position) except ValueError: #Not a proper int. diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 2b2135e4a3..9f51591a58 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -50,6 +50,8 @@ class MachineManager(QObject): Preferences.getInstance().addPreference("cura/active_machine", "") + self._global_event_keys = set() + active_machine_id = Preferences.getInstance().getValue("cura/active_machine") self._printer_output_devices = [] @@ -58,7 +60,9 @@ class MachineManager(QObject): if active_machine_id != "": # An active machine was saved, so restore it. self.setActiveMachine(active_machine_id) - pass + if self._global_container_stack and self._global_container_stack.getProperty("machine_extruder_count", "value") > 1: + # Make sure _active_container_stack is properly initiated + ExtruderManager.getInstance().setActiveExtruderIndex(0) self._auto_change_material_hotend_flood_window = 10 # The minimum number of seconds between asking if the material or hotend on the machine should be used self._auto_change_material_hotend_flood_time = 0 # The last timestamp (in seconds) when the user was asked about changing the material or hotend to whatis loaded on the machine @@ -197,7 +201,85 @@ class MachineManager(QObject): def _onGlobalPropertyChanged(self, key, property_name): if property_name == "value": + ## We can get recursion issues. So we store a list of keys that we are still handling to prevent this. + if key in self._global_event_keys: + return + self._global_event_keys.add(key) self.globalValueChanged.emit() + + if self._active_container_stack and self._active_container_stack != self._global_container_stack: + # Make the global current settings mirror the stack values appropriate for this setting + if self._active_container_stack.getProperty("extruder_nr", "value") == int(self._active_container_stack.getProperty(key, "global_inherits_stack")): + + new_value = self._active_container_stack.getProperty(key, "value") + self._global_container_stack.getTop().setProperty(key, "value", new_value) + + # Global-only setting values should be set on all extruders and the global stack + if not self._global_container_stack.getProperty(key, "settable_per_extruder"): + extruder_stacks = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())) + target_stack_position = int(self._active_container_stack.getProperty(key, "global_inherits_stack")) + if target_stack_position == -1: # Prevent -1 from selecting wrong stack. + target_stack = self._active_container_stack + else: + target_stack = extruder_stacks[target_stack_position] + new_value = target_stack.getProperty(key, "value") + target_stack_has_user_value = target_stack.getTop().getInstance(key) != None + for extruder_stack in extruder_stacks: + if extruder_stack != target_stack: + if target_stack_has_user_value: + extruder_stack.getTop().setProperty(key, "value", new_value) + else: + # Remove from the value from the other stacks as well, unless the + # top value from the other stacklevels is different than the new value + for container in extruder_stack.getContainers(): + if container.__class__ == UM.Settings.InstanceContainer and container.getInstance(key) != None: + if container.getProperty(key, "value") != new_value: + # It could be that the setting needs to be removed instead of updated. + temp = extruder_stack + containers = extruder_stack.getContainers() + # Ensure we have the entire 'chain' + while temp.getNextStack(): + temp = temp.getNextStack() + containers.extend(temp.getContainers()) + instance_needs_removal = False + + if len(containers) > 1: + for index in range(1, len(containers)): + deeper_container = containers[index] + if deeper_container.getProperty(key, "value") is None: + continue # Deeper container does not have the value, so continue. + if deeper_container.getProperty(key, "value") == new_value: + # Removal will result in correct value, so do that. + # We do this to prevent the reset from showing up unneeded. + instance_needs_removal = True + break + else: + # Container has the value, but it's not the same. Stop looking. + break + if instance_needs_removal: + extruder_stack.getTop().removeInstance(key) + else: + extruder_stack.getTop().setProperty(key, "value", new_value) + else: + # Check if we really need to remove something. + if extruder_stack.getProperty(key, "value") != new_value: + extruder_stack.getTop().removeInstance(key) + break + if self._global_container_stack.getProperty(key, "value") != new_value: + self._global_container_stack.getTop().setProperty(key, "value", new_value) + self._global_event_keys.remove(key) + + if property_name == "global_inherits_stack": + if self._active_container_stack and self._active_container_stack != self._global_container_stack: + # Update the global user value when the "global_inherits_stack" function points to a different stack + stack_index = int(self._active_container_stack.getProperty(key, property_name)) + extruder_stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())] + + if len(extruder_stacks) > stack_index: + new_value = extruder_stacks[stack_index].getProperty(key, "value") + if self._global_container_stack.getProperty(key, "value") != new_value: + self._global_container_stack.getTop().setProperty(key, "value", new_value) + if property_name == "validationState": if self._global_stack_valid: changed_validation_state = self._active_container_stack.getProperty(key, property_name) @@ -209,7 +291,6 @@ class MachineManager(QObject): if not has_errors: self._global_stack_valid = True self.globalValidationChanged.emit() - def _onGlobalContainerChanged(self): if self._global_container_stack: self._global_container_stack.nameChanged.disconnect(self._onMachineNameChanged) @@ -233,7 +314,7 @@ class MachineManager(QObject): self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged) self._global_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged) self._global_stack_valid = not self._checkStackForErrors(self._global_container_stack) - + self.globalValidationChanged.emit() material = self._global_container_stack.findContainer({"type": "material"}) material.nameChanged.connect(self._onMaterialNameChanged) @@ -254,6 +335,18 @@ class MachineManager(QObject): def _onInstanceContainersChanged(self, container): container_type = container.getMetaDataEntry("type") + + if self._active_container_stack and self._active_container_stack != self._global_container_stack: + if int(self._active_container_stack.getProperty("extruder_nr", "value")) == 0: + global_container = self._global_container_stack.findContainer({"type": container_type}) + if global_container and global_container != container: + container_index = self._global_container_stack.getContainerIndex(global_container) + self._global_container_stack.replaceContainer(container_index, container) + + for key in container.getAllKeys(): + # Make sure the values in this profile are distributed to other stacks if necessary + self._onGlobalPropertyChanged(key, "value") + if container_type == "material": self.activeMaterialChanged.emit() elif container_type == "variant": @@ -269,13 +362,14 @@ class MachineManager(QObject): @pyqtSlot(str, str) def addMachine(self, name, definition_id): - definitions = UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id = definition_id) + container_registry = UM.Settings.ContainerRegistry.getInstance() + definitions = container_registry.findDefinitionContainers(id = definition_id) if definitions: definition = definitions[0] name = self._createUniqueName("machine", "", name, definition.getName()) new_global_stack = UM.Settings.ContainerStack(name) new_global_stack.addMetaDataEntry("type", "machine") - UM.Settings.ContainerRegistry.getInstance().addContainer(new_global_stack) + container_registry.addContainer(new_global_stack) variant_instance_container = self._updateVariantContainer(definition) material_instance_container = self._updateMaterialContainer(definition, variant_instance_container) @@ -285,7 +379,7 @@ class MachineManager(QObject): current_settings_instance_container.addMetaDataEntry("machine", name) current_settings_instance_container.addMetaDataEntry("type", "user") current_settings_instance_container.setDefinition(definitions[0]) - UM.Settings.ContainerRegistry.getInstance().addContainer(current_settings_instance_container) + container_registry.addContainer(current_settings_instance_container) # If a definition is found, its a list. Should only have one item. new_global_stack.addContainer(definition) @@ -418,6 +512,19 @@ class MachineManager(QObject): return True return containers[0].isReadOnly() + ## Copy the value of the setting of the current extruder to all other extruders as well as the global container. + @pyqtSlot(str) + def copyValueToExtruders(self, key): + if not self._active_container_stack or self._global_container_stack.getProperty("machine_extruder_count", "value") <= 1: + return + + new_value = self._active_container_stack.getProperty(key, "value") + stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())] + stacks.append(self._global_container_stack) + for extruder_stack in stacks: + if extruder_stack != self._active_container_stack and extruder_stack.getProperty(key, "value") != new_value: + extruder_stack.getTop().setProperty(key, "value", new_value) + @pyqtSlot(result = str) def newQualityContainerFromQualityAndUser(self): new_container_id = self.duplicateContainer(self.activeQualityId) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 2a51e442da..57d76b2783 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -111,6 +111,8 @@ class ThreeMFReader(MeshReader): if len(objects) > 1: group_decorator = GroupDecorator() result.addDecorator(group_decorator) + elif len(objects) == 1: + result = result.getChildren()[0] # Only one object found, return that. except Exception as e: Logger.log("e", "exception occured in 3mf reader: %s", e) diff --git a/plugins/LegacyProfileReader/DictionaryOfDoom.json b/plugins/LegacyProfileReader/DictionaryOfDoom.json index 9dd0c04a05..471604ddbc 100644 --- a/plugins/LegacyProfileReader/DictionaryOfDoom.json +++ b/plugins/LegacyProfileReader/DictionaryOfDoom.json @@ -47,7 +47,7 @@ "adhesion_type": "\"skirt\" if (platform_adhesion == \"None\") else platform_adhesion.lower()", "skirt_line_count": "skirt_line_count", "skirt_gap": "skirt_gap", - "skirt_minimal_length": "skirt_minimal_length", + "skirt_brim_minimal_length": "skirt_minimal_length", "brim_line_count": "brim_line_count", "raft_margin": "raft_margin", "raft_airgap": "float(raft_airgap_all) + float(raft_airgap)", diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index 546b7086e6..79f10f508f 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -181,7 +181,9 @@ Item { onLoaded: { settingLoader.item.showRevertButton = false settingLoader.item.showInheritButton = false + settingLoader.item.showLinkedSettingIcon = false settingLoader.item.doDepthIndentation = false + settingLoader.item.doQualityUserSettingEmphasis = false } sourceComponent: diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 913ad467c5..d50194d40f 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -10,6 +10,7 @@ from UM.Message import Message from UM.i18n import i18nCatalog from UM.Logger import Logger from UM.Platform import Platform +from UM.Qt.Duration import DurationFormat import collections import json @@ -90,7 +91,7 @@ class SliceInfo(Extension): "settings": global_container_stack.serialize(), # global_container with references on used containers "version": Application.getInstance().getVersion(), "modelhash": "None", - "printtime": print_information.currentPrintTime.getDisplayString(), + "printtime": print_information.currentPrintTime.getDisplayString(DurationFormat.Format.ISO8601), "filament": material_used, "language": Preferences.getInstance().getValue("general/language"), } @@ -126,4 +127,4 @@ class SliceInfo(Extension): except: # We really can't afford to have a mistake here, as this would break the sending of g-code to a device # (Either saving or directly to a printer). The functionality of the slice data is not *that* important. - pass \ No newline at end of file + pass diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index 15cf25e65e..96f60fb7cf 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -34,9 +34,10 @@ class SolidView(View): self._enabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader")) if not self._disabled_shader: - self._disabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader")) - self._disabled_shader.setUniformValue("u_diffuseColor", [0.68, 0.68, 0.68, 1.0]) - self._disabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0))) + self._disabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "striped.shader")) + self._disabled_shader.setUniformValue("u_diffuseColor1", [1.0, 0.28, 0.28, 1.0]) + self._disabled_shader.setUniformValue("u_diffuseColor2", [0.68, 0.68, 0.68, 1.0]) + self._disabled_shader.setUniformValue("u_width", 50.0) if Application.getInstance().getGlobalContainerStack(): if Preferences.getInstance().getValue("view/show_overhang"): diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index da3dfa2bde..7bd5b66cc2 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -8,14 +8,12 @@ import time import queue import re import functools -import os.path from UM.Application import Application from UM.Logger import Logger -from UM.PluginRegistry import PluginRegistry from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState +from UM.Message import Message -from PyQt5.QtQml import QQmlComponent, QQmlContext from PyQt5.QtCore import QUrl, pyqtSlot, pyqtSignal from UM.i18n import i18nCatalog @@ -137,7 +135,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): # \param gcode_list List with gcode (strings). def printGCode(self, gcode_list): if self._progress or self._connection_state != ConnectionState.connected: - self._error_message = Message(i18n_catalog.i18nc("@info:status", "Printer is busy or not connected. Unable to start a new job.")) + self._error_message = Message(catalog.i18nc("@info:status", "Printer is busy or not connected. Unable to start a new job.")) self._error_message.show() Logger.log("d", "Printer is busy or not connected, aborting print") self.writeError.emit(self) @@ -504,6 +502,13 @@ class USBPrinterOutputDevice(PrinterOutputDevice): # It will be normalized (based on max_progress) to range 0 - 100 def setProgress(self, progress, max_progress = 100): self._progress = (progress / max_progress) * 100 # Convert to scale of 0-100 + if self._progress == 100: + # Printing is done, reset progress + self._gcode_position = 0 + self.setProgress(0) + self._is_printing = False + self._is_paused = False + self._updateJobState("ready") self.progressChanged.emit() ## Cancel the current print. Printer connection wil continue to listen. diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 2493e23405..64ea1ac665 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -31,6 +31,9 @@ _setting_name_translations = { "remove_overlapping_walls_enabled": "travel_compensate_overlapping_walls_enabled", "remove_overlapping_walls_x_enabled": "travel_compensate_overlapping_walls_x_enabled", "retraction_hop": "retraction_hop_enabled", + "skirt_line_width": "skirt_brim_line_width", + "skirt_minimal_length": "skirt_brim_minimal_length", + "skirt_speed": "skirt_brim_speed", "speed_support_lines": "speed_support_infill" } @@ -157,6 +160,15 @@ class VersionUpgrade21to22(VersionUpgrade): elif key == "retraction_hop": #Setting key was changed. del settings[key] settings["retraction_hop_enabled"] = value + elif key == "skirt_minimal_length": #Setting key was changed. + del settings[key] + settings["skirt_brim_minimal_length"] = value + elif key == "skirt_line_width": #Setting key was changed. + del settings[key] + settings["skirt_brim_line_width"] = value + elif key == "skirt_speed": #Setting key was changed. + del settings[key] + settings["skirt_brim_speed"] = value elif key == "speed_support_lines": #Setting key was changed. del settings[key] settings["speed_support_infill"] = value diff --git a/resources/definitions/bq_hephestos_2.def.json b/resources/definitions/bq_hephestos_2.def.json index 51777ff83e..e49e7163e8 100644 --- a/resources/definitions/bq_hephestos_2.def.json +++ b/resources/definitions/bq_hephestos_2.def.json @@ -38,9 +38,9 @@ "speed_wall_0": { "default_value": 30 }, "speed_infill": { "default_value": 80 }, "speed_topbottom": { "default_value": 35 }, - "skirt_speed": { "default_value": 35 }, + "skirt_brim_speed": { "default_value": 35 }, "skirt_line_count": { "default_value": 4 }, - "skirt_minimal_length": { "default_value": 30 }, + "skirt_brim_minimal_length": { "default_value": 30 }, "skirt_gap": { "default_value": 6 }, "cool_fan_full_at_height": { "default_value": 0.4 } } diff --git a/resources/definitions/bq_witbox_2.def.json b/resources/definitions/bq_witbox_2.def.json index b9d9b497cd..e47d082d0f 100644 --- a/resources/definitions/bq_witbox_2.def.json +++ b/resources/definitions/bq_witbox_2.def.json @@ -89,13 +89,13 @@ "speed_topbottom": { "default_value": 35 }, - "skirt_speed": { + "skirt_brim_speed": { "default_value": 35 }, "skirt_line_count": { "default_value": 4 }, - "skirt_minimal_length": { + "skirt_brim_minimal_length": { "default_value": 30 }, "skirt_gap": { diff --git a/resources/definitions/fdmextruder.def.json b/resources/definitions/fdmextruder.def.json index 7ce44b77b0..bde24ee684 100644 --- a/resources/definitions/fdmextruder.def.json +++ b/resources/definitions/fdmextruder.def.json @@ -153,7 +153,7 @@ "unit": "mm", "default_value": 0, "minimum_value_warning": "0", - "maximum_value_warning": "machine_height", + "maximum_value": "machine_height", "settable_per_mesh": false, "settable_per_extruder": true } @@ -175,7 +175,7 @@ "unit": "mm", "default_value": 0, "minimum_value_warning": "machine_nozzle_offset_x", - "maximum_value_warning": "machine_width", + "maximum_value": "machine_width", "settable_per_mesh": false, "settable_per_extruder": true, "enabled": false diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 973645259d..950c856a6e 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -342,7 +342,7 @@ "unit": "mm", "default_value": 0, "minimum_value_warning": "0", - "maximum_value_warning": "machine_height", + "maximum_value": "machine_height", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -603,10 +603,10 @@ "value": "line_width", "settable_per_mesh": true }, - "skirt_line_width": + "skirt_brim_line_width": { - "label": "Skirt Line Width", - "description": "Width of a single skirt line.", + "label": "Skirt/Brim Line Width", + "description": "Width of a single skirt or brim line.", "unit": "mm", "minimum_value": "0.0001", "minimum_value_warning": "0.2", @@ -614,6 +614,7 @@ "default_value": 0.4, "type": "float", "value": "line_width", + "enabled": "adhesion_type == \"skirt\" or adhesion_type == \"brim\"", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -629,6 +630,7 @@ "type": "float", "enabled": "support_enable", "value": "line_width", + "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": false, "settable_per_extruder": false }, @@ -643,6 +645,7 @@ "type": "float", "enabled": "support_roof_enable", "value": "line_width", + "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": false, "settable_per_extruder": false }, @@ -706,7 +709,7 @@ "default_value": 0.8, "minimum_value": "0", "minimum_value_warning": "0.6", - "maximum_value_warning": "machine_height", + "maximum_value": "machine_height", "type": "float", "settable_per_mesh": true, "children": @@ -718,7 +721,7 @@ "unit": "mm", "default_value": 0.8, "minimum_value": "0", - "maximum_value_warning": "machine_height", + "maximum_value": "machine_height", "type": "float", "value": "top_bottom_thickness", "settable_per_mesh": true, @@ -746,7 +749,7 @@ "minimum_value": "0", "type": "float", "value": "top_bottom_thickness", - "maximum_value_warning": "machine_height", + "maximum_value": "machine_height", "settable_per_mesh": true, "children": { @@ -1441,6 +1444,7 @@ "maximum_value_warning": "150", "default_value": 60, "value": "speed_print", + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": false, "settable_per_extruder": false, @@ -1457,6 +1461,7 @@ "maximum_value": "299792458000", "maximum_value_warning": "150", "value": "speed_support", + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": false, "settable_per_extruder": false @@ -1473,6 +1478,7 @@ "maximum_value_warning": "150", "enabled": "support_roof_enable and support_enable", "value": "speed_support / 1.5", + "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": false, "settable_per_extruder": false } @@ -1519,9 +1525,9 @@ "maximum_value_warning": "300", "settable_per_mesh": true }, - "skirt_speed": { - "label": "Skirt Speed", - "description": "The speed at which the skirt and brim are printed. Normally this is done at the initial layer speed, but sometimes you might want to print the skirt at a different speed.", + "skirt_brim_speed": { + "label": "Skirt/Brim Speed", + "description": "The speed at which the skirt and brim are printed. Normally this is done at the initial layer speed, but sometimes you might want to print the skirt or brim at a different speed.", "unit": "mm/s", "type": "float", "default_value": 30, @@ -1529,6 +1535,7 @@ "maximum_value": "299792458000", "maximum_value_warning": "300", "value": "speed_layer_0", + "enabled": "adhesion_type == \"skirt\" or adhesion_type == \"brim\"", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -1643,6 +1650,7 @@ "maximum_value_warning": "10000", "default_value": 3000, "value": "acceleration_print", + "global_inherits_stack": "support_extruder_nr", "enabled": "acceleration_enabled and support_enable", "settable_per_mesh": false, "settable_per_extruder": false, @@ -1654,6 +1662,7 @@ "type": "float", "default_value": 3000, "value": "acceleration_support", + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0.1", "minimum_value_warning": "100", "maximum_value_warning": "10000", @@ -1668,6 +1677,7 @@ "type": "float", "default_value": 3000, "value": "acceleration_support", + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0.1", "minimum_value_warning": "100", "maximum_value_warning": "10000", @@ -1718,9 +1728,9 @@ "enabled": "acceleration_enabled", "settable_per_mesh": true }, - "acceleration_skirt": { - "label": "Skirt Acceleration", - "description": "The acceleration with which the skirt and brim are printed. Normally this is done with the initial layer acceleration, but sometimes you might want to print the skirt at a different acceleration.", + "acceleration_skirt_brim": { + "label": "Skirt/Brim Acceleration", + "description": "The acceleration with which the skirt and brim are printed. Normally this is done with the initial layer acceleration, but sometimes you might want to print the skirt or brim at a different acceleration.", "unit": "mm/s²", "type": "float", "default_value": 3000, @@ -1831,6 +1841,7 @@ "maximum_value_warning": "50", "default_value": 20, "value": "jerk_print", + "global_inherits_stack": "support_extruder_nr", "enabled": "jerk_enabled and support_enable", "settable_per_mesh": false, "settable_per_extruder": false, @@ -1842,6 +1853,7 @@ "type": "float", "default_value": 20, "value": "jerk_support", + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0.1", "minimum_value_warning": "5", "maximum_value_warning": "50", @@ -1856,6 +1868,7 @@ "type": "float", "default_value": 20, "value": "jerk_support", + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0.1", "minimum_value_warning": "5", "maximum_value_warning": "50", @@ -1906,8 +1919,8 @@ "enabled": "jerk_enabled", "settable_per_mesh": true }, - "jerk_skirt": { - "label": "Skirt Jerk", + "jerk_skirt_brim": { + "label": "Skirt/Brim Jerk", "description": "The maximum instantaneous velocity change with which the skirt and brim are printed.", "unit": "mm/s", "type": "float", @@ -2120,7 +2133,8 @@ "description": "Enable support structures. These structures support parts of the model with severe overhangs.", "type": "bool", "default_value": false, - "settable_per_mesh": true + "settable_per_mesh": true, + "settable_per_extruder": false }, "support_type": { @@ -2146,6 +2160,7 @@ "minimum_value": "0", "maximum_value": "90", "default_value": 50, + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": true }, @@ -2214,6 +2229,7 @@ "minimum_value": "0", "maximum_value_warning": "10", "default_value": 0.15, + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": true, "children": @@ -2229,6 +2245,7 @@ "type": "float", "enabled": "support_enable", "value": "support_z_distance", + "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": true }, "support_bottom_distance": @@ -2240,6 +2257,7 @@ "maximum_value_warning": "10", "default_value": 0.1, "value": "0.1 if support_type == 'everywhere' else 0", + "global_inherits_stack": "support_extruder_nr", "type": "float", "enabled": "support_enable and support_type == 'everywhere'", "settable_per_mesh": true @@ -2255,6 +2273,7 @@ "minimum_value": "0", "maximum_value_warning": "10", "default_value": 0.7, + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": true }, @@ -2267,6 +2286,7 @@ "z_overrides_xy": "Z overrides X/Y" }, "default_value": "z_overrides_xy", + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": true }, @@ -2279,6 +2299,7 @@ "maximum_value_warning": "10", "default_value": 0.2, "value": "machine_nozzle_size / 2", + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable and support_xy_overrides_z=='z_overrides_xy'", "settable_per_mesh": true }, @@ -2289,6 +2310,7 @@ "unit": "mm", "type": "float", "default_value": 0.3, + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0", "maximum_value_warning": "1.0", "enabled": "support_enable", @@ -2301,6 +2323,7 @@ "unit": "mm", "type": "float", "default_value": 2.0, + "global_inherits_stack": "support_extruder_nr", "minimum_value_warning": "0", "maximum_value_warning": "10", "enabled": "support_enable", @@ -2313,6 +2336,7 @@ "unit": "mm", "type": "float", "default_value": 0.2, + "global_inherits_stack": "support_extruder_nr", "minimum_value_warning": "-0.5", "maximum_value_warning": "5.0", "enabled": "support_enable", @@ -2325,6 +2349,7 @@ "unit": "mm", "type": "float", "default_value": 0.6, + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0", "maximum_value_warning": "1.0", "enabled": "support_enable", @@ -2336,6 +2361,7 @@ "description": "Generate a dense top skin at the top of the support on which the model is printed.", "type": "bool", "default_value": false, + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": true }, @@ -2347,6 +2373,7 @@ "type": "float", "default_value": 1, "minimum_value": "0", + "global_inherits_stack": "support_extruder_nr", "maximum_value_warning": "10", "enabled": "support_roof_enable and support_enable", "settable_per_mesh": true @@ -2404,6 +2431,7 @@ "description": "Use specialized towers to support tiny overhang areas. These towers have a larger diameter than the region they support. Near the overhang the towers' diameter decreases, forming a roof.", "type": "bool", "default_value": true, + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": true }, @@ -2414,6 +2442,7 @@ "unit": "mm", "type": "float", "default_value": 3.0, + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0", "maximum_value_warning": "10", "enabled": "support_enable and support_use_towers", @@ -2426,6 +2455,7 @@ "unit": "mm", "type": "float", "default_value": 3.0, + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0", "maximum_value_warning": "10", "maximum_value": "support_tower_diameter", @@ -2441,6 +2471,7 @@ "minimum_value": "0", "maximum_value": "90", "default_value": 65, + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable and support_use_towers", "settable_per_mesh": true } @@ -2520,17 +2551,17 @@ "settable_per_mesh": false, "settable_per_extruder": true }, - "skirt_minimal_length": + "skirt_brim_minimal_length": { - "label": "Skirt Minimum Length", - "description": "The minimum length of the skirt. If this length is not reached by the skirt line count, more skirt lines will be added until the minimum length is reached. Note: If the line count is set to 0 this is ignored.", + "label": "Skirt/Brim Minimum Length", + "description": "The minimum length of the skirt or brim. If this length is not reached by all skirt or brim lines together, more skirt or brim lines will be added until the minimum length is reached. Note: If the line count is set to 0 this is ignored.", "unit": "mm", "type": "float", "default_value": 250, "minimum_value": "0", "minimum_value_warning": "25", "maximum_value_warning": "2500", - "enabled": "adhesion_type == \"skirt\"", + "enabled": "adhesion_type == \"skirt\" or adhesion_type == \"brim\"", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -2556,7 +2587,7 @@ "default_value": 20, "minimum_value": "0", "maximum_value_warning": "300", - "value": "math.ceil(brim_width / skirt_line_width)", + "value": "math.ceil(brim_width / skirt_brim_line_width)", "enabled": "adhesion_type == \"brim\"", "settable_per_mesh": false, "settable_per_extruder": true @@ -2596,7 +2627,7 @@ "value": "raft_airgap / 2", "minimum_value": "0", "maximum_value_warning": "layer_height", - "enabled": "adhesion_type == 'raft'", + "enabled": "adhesion_type == \"raft\"", "settable_per_mesh": false, "settable_per_extruder": true }, diff --git a/resources/definitions/maker_starter.def.json b/resources/definitions/maker_starter.def.json index a26ca058f0..4d3f6e06b7 100644 --- a/resources/definitions/maker_starter.def.json +++ b/resources/definitions/maker_starter.def.json @@ -94,7 +94,7 @@ "speed_layer_0": { "default_value": 20 }, - "skirt_speed": { + "skirt_brim_speed": { "default_value": 15 }, "speed_slowdown_layers": { diff --git a/resources/definitions/mankati_fullscale_xt_plus.def.json b/resources/definitions/mankati_fullscale_xt_plus.def.json index 57742696fb..eb8b99aaf9 100644 --- a/resources/definitions/mankati_fullscale_xt_plus.def.json +++ b/resources/definitions/mankati_fullscale_xt_plus.def.json @@ -61,6 +61,6 @@ "cool_fan_speed": { "default": 0 }, "skirt_line_count": { "default": 3 }, "skirt_gap": { "default": 4 }, - "skirt_minimal_length": { "default": 200 } + "skirt_brim_minimal_length": { "default": 200 } } } diff --git a/resources/definitions/rigidbot.def.json b/resources/definitions/rigidbot.def.json index ace8300d5d..fb8112a582 100644 --- a/resources/definitions/rigidbot.def.json +++ b/resources/definitions/rigidbot.def.json @@ -94,7 +94,7 @@ "default_value": 4, "enabled": "adhesion_type == \"Skirt\"" }, - "skirt_minimal_length": { + "skirt_brim_minimal_length": { "default_value": 200, "enabled": "adhesion_type == \"Skirt\"" } diff --git a/resources/definitions/rigidbot_big.def.json b/resources/definitions/rigidbot_big.def.json index ce27d36745..7026df646b 100644 --- a/resources/definitions/rigidbot_big.def.json +++ b/resources/definitions/rigidbot_big.def.json @@ -97,7 +97,7 @@ "default_value": 4, "enabled": "adhesion_type == \"Skirt\"" }, - "skirt_minimal_length": { + "skirt_brim_minimal_length": { "default_value": 200, "enabled": "adhesion_type == \"Skirt\"" } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index c449b4c83e..d0870991d2 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -321,23 +321,7 @@ UM.MainWindow sourceSize.height: height; } - Button - { - id: viewModeButton - anchors - { - top: toolbar.bottom; - topMargin: UM.Theme.getSize("window_margin").height; - left: parent.left; - } - text: catalog.i18nc("@action:button","View Mode"); - iconSource: UM.Theme.getIcon("viewmode"); - - style: UM.Theme.styles.tool_button; - tooltip: ''; - menu: ViewMenu { } - } Toolbar { @@ -367,6 +351,24 @@ UM.MainWindow width: UM.Theme.getSize("sidebar").width; } + Button + { + id: viewModeButton + + anchors + { + top: toolbar.bottom; + topMargin: UM.Theme.getSize("window_margin").height; + left: parent.left; + } + text: catalog.i18nc("@action:button","View Mode"); + iconSource: UM.Theme.getIcon("viewmode"); + + style: UM.Theme.styles.tool_button; + tooltip: ''; + menu: ViewMenu { } + } + Rectangle { id: viewportOverlay diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 8e14241741..9064c4835f 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -195,15 +195,21 @@ Rectangle { color: UM.Theme.getColor("text_subtext") text: { - var amounts = []; + var lengths = []; + var weights = []; if(base.printMaterialLengths) { for(var index = 0; index < base.printMaterialLengths.length; index++) { - amounts.push(base.printMaterialLengths[index].toFixed(2)); + if(base.printMaterialLengths[index] > 0) { + lengths.push(base.printMaterialLengths[index].toFixed(2)); + weights.push(String(Math.floor(base.printMaterialWeights[index]))); + } } - } else { - amounts = ["0.00"]; } - return catalog.i18nc("@label", "%1 m").arg(amounts.join(" + ")); + if(lengths.length == 0) { + lengths = ["0.00"]; + weights = ["0"]; + } + return catalog.i18nc("@label", "%1 m / %2 g").arg(lengths.join(" + ")).arg(weights.join(" + ")); } } } diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml index a7bdabb3c5..eb6c37f73c 100644 --- a/resources/qml/Settings/SettingItem.qml +++ b/resources/qml/Settings/SettingItem.qml @@ -21,10 +21,13 @@ Item { property var showRevertButton: true property var showInheritButton: true + property var showLinkedSettingIcon: true property var doDepthIndentation: true + property var doQualityUserSettingEmphasis: true // Create properties to put property provider stuff in (bindings break in qt 5.5.1 otherwise) property var state: propertyProvider.properties.state + property var settablePerExtruder: propertyProvider.properties.settable_per_extruder property var stackLevels: propertyProvider.stackLevels property var stackLevel: stackLevels[0] @@ -115,7 +118,7 @@ Item { color: UM.Theme.getColor("setting_control_text"); // emphasize the setting if it has a value in the user or quality profile - font: base.stackLevel != undefined && base.stackLevel <= 1 ? UM.Theme.getFont("default_italic") : UM.Theme.getFont("default") + font: base.doQualityUserSettingEmphasis && base.stackLevel != undefined && base.stackLevel <= 1 ? UM.Theme.getFont("default_italic") : UM.Theme.getFont("default") } Row @@ -131,6 +134,26 @@ Item { verticalCenter: parent.verticalCenter } + UM.SimpleButton + { + id: linkedSettingIcon; + + visible: base.settablePerExtruder != "True" && base.showLinkedSettingIcon + + height: parent.height; + width: height; + + backgroundColor: UM.Theme.getColor("setting_control"); + hoverBackgroundColor: UM.Theme.getColor("setting_control") + color: UM.Theme.getColor("setting_control_button") + hoverColor: UM.Theme.getColor("setting_control_button") + + iconSource: UM.Theme.getIcon("link") + + onEntered: { hoverTimer.stop(); base.showTooltip(catalog.i18nc("@label", "This setting is always shared between all extruders. Changing it here will change the value for all extruders")) } + onExited: base.showTooltip(base.tooltipText); + } + UM.SimpleButton { id: revertButton; @@ -231,7 +254,6 @@ Item { onEntered: { hoverTimer.stop(); base.showTooltip(catalog.i18nc("@label", "This setting is normally calculated, but it currently has an absolute value set.\n\nClick to restore the calculated value.")) } onExited: base.showTooltip(base.tooltipText); } - } Item diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 39f0f833b8..009dbf09a9 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -33,15 +33,6 @@ ScrollView exclude: ["machine_settings"] expanded: Printer.expandedCategories onExpandedChanged: Printer.setExpandedCategories(expanded) - - filter: - { - if(ExtruderManager.activeExtruderStackId) - { - return { "settable_per_extruder": true } - } - return { } - } } delegate: Loader @@ -53,7 +44,15 @@ ScrollView Behavior on height { NumberAnimation { duration: 100 } } opacity: provider.properties.enabled == "True" ? 1 : 0 Behavior on opacity { NumberAnimation { duration: 100 } } - enabled: provider.properties.enabled == "True" + enabled: + { + if(!ExtruderManager.activeExtruderStackId && ExtruderManager.extruderCount > 0) + { + // disable all controls on the global tab, except categories + return model.type == "category" + } + return provider.properties.enabled == "True" + } property var definition: model property var settingDefinitionsModel: definitionsModel @@ -88,20 +87,59 @@ ScrollView } } + // Binding to ensure that the right containerstack ID is set for the provider. + // This ensures that if a setting has a global_inherits_stack id (for instance; Support speed points to the + // extruder that actually prints the support, as that is the setting we need to use to calculate the value) + Binding + { + target: provider + property: "containerStackId" + value: + { + if(inheritStackProvider.properties.global_inherits_stack == -1 || inheritStackProvider.properties.global_inherits_stack == null) + { + if( ExtruderManager.activeExtruderStackId) + { + return ExtruderManager.activeExtruderStackId + } + else + { + return Cura.MachineManager.activeMachineId + } + } + return ExtruderManager.extruderIds[String(inheritStackProvider.properties.global_inherits_stack)] + } + } + + // 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. + UM.SettingPropertyProvider + { + id: inheritStackProvider + containerStackId: Cura.MachineManager.activeMachineId + key: model.key + watchedProperties: [ "global_inherits_stack"] + } + UM.SettingPropertyProvider { id: provider - containerStackId: ExtruderManager.activeExtruderStackId ? ExtruderManager.activeExtruderStackId : Cura.MachineManager.activeMachineId + containerStackId: delegate.stackId key: model.key ? model.key : "" - watchedProperties: [ "value", "enabled", "state", "validationState" ] + watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder" ] storeIndex: 0 } Connections { target: item - onContextMenuRequested: { contextMenu.key = model.key; contextMenu.popup() } + onContextMenuRequested: + { + contextMenu.key = model.key; + contextMenu.provider = provider + contextMenu.popup(); + } onShowTooltip: base.showTooltip(delegate, { x: 0, y: delegate.height / 2 }, text) onHideTooltip: base.hideTooltip() } @@ -133,9 +171,24 @@ ScrollView Menu { - id: contextMenu; + id: contextMenu - property string key; + property string key + property var provider + + MenuItem + { + //: Settings context menu action + text: catalog.i18nc("@action:menu", "Copy value to all extruders") + visible: machineExtruderCount.properties.value > 1 + enabled: contextMenu.provider != undefined && contextMenu.provider.properties.settable_per_extruder != "False" + onTriggered: Cura.MachineManager.copyValueToExtruders(contextMenu.key) + } + + MenuSeparator + { + visible: machineExtruderCount.properties.value > 1 + } MenuItem { @@ -151,5 +204,15 @@ ScrollView onTriggered: Cura.Actions.configureSettingVisibility.trigger(contextMenu); } } + + UM.SettingPropertyProvider + { + id: machineExtruderCount + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_extruder_count" + watchedProperties: [ "value" ] + storeIndex: 0 + } } } diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index aa6f2c0067..1f7fa00b54 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -84,15 +84,15 @@ Column orientation: ListView.Horizontal - model: Cura.ExtrudersModel { id: extrudersModel; addGlobal: true } + model: Cura.ExtrudersModel { id: extrudersModel; addGlobal: false } Connections { target: Cura.MachineManager onGlobalContainerChanged: { - base.currentExtruderIndex = -1; - forceActiveFocus() + forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values. + base.currentExtruderIndex = (machineExtruderCount.properties.value == 1) ? -1 : 0; ExtruderManager.setActiveExtruderIndex(base.currentExtruderIndex); } } @@ -110,7 +110,7 @@ Column onClicked: { - forceActiveFocus() //Changing focus applies the currently-being-typed values so it can change the displayed setting values. + forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values. base.currentExtruderIndex = index; ExtruderManager.setActiveExtruderIndex(index); } @@ -258,6 +258,8 @@ Column { id: globalProfileSelection text: Cura.MachineManager.activeQualityName + enabled: !extrudersList.visible || base.currentExtruderIndex > -1 + width: parent.width * 0.55 + UM.Theme.getSize("default_margin").width height: UM.Theme.getSize("setting_control").height tooltip: Cura.MachineManager.activeQualityName diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index a393500fb7..0e1b04cdeb 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -19,6 +19,7 @@ Item property Action configureSettings; property variant minimumPrintTime: PrintInformation.minimumPrintTime; property variant maximumPrintTime: PrintInformation.maximumPrintTime; + property bool settingsEnabled: ExtruderManager.activeExtruderStackId || ExtruderManager.extruderCount == 0 Component.onCompleted: PrintInformation.enabled = true Component.onDestruction: PrintInformation.enabled = false @@ -81,7 +82,11 @@ Item height: width border.color: { - if(infillListView.activeIndex == index) + if(!base.settingsEnabled) + { + return UM.Theme.getColor("setting_control_disabled_border") + } + else if(infillListView.activeIndex == index) { return UM.Theme.getColor("setting_control_selected") } @@ -92,7 +97,17 @@ Item return UM.Theme.getColor("setting_control_border") } border.width: UM.Theme.getSize("default_lining").width - color: infillListView.activeIndex == index ? UM.Theme.getColor("setting_control_selected") : "transparent" + color: { + if(infillListView.activeIndex == index) + { + if(!base.settingsEnabled) + { + return UM.Theme.getColor("setting_control_disabled_text") + } + return UM.Theme.getColor("setting_control_selected") + } + return "transparent" + } UM.RecolorImage { id: infillIcon @@ -102,13 +117,24 @@ Item sourceSize.width: width sourceSize.height: width source: UM.Theme.getIcon(model.icon); - color: (infillListView.activeIndex == index) ? UM.Theme.getColor("text_white") : UM.Theme.getColor("text") + color: { + if(infillListView.activeIndex == index) + { + return UM.Theme.getColor("text_reversed") + } + if(!base.settingsEnabled) + { + return UM.Theme.getColor("setting_control_disabled_text") + } + return UM.Theme.getColor("text") + } } MouseArea { id: infillMouseArea anchors.fill: parent hoverEnabled: true + enabled: base.settingsEnabled onClicked: { if (infillListView.activeIndex != index) { @@ -125,6 +151,7 @@ Item } Label{ id: infillLabel + font: UM.Theme.getFont("default") anchors.top: infillIconLining.bottom anchors.horizontalCenter: infillIconLining.horizontalCenter color: infillListView.activeIndex == index ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_border") @@ -205,6 +232,7 @@ Item //: Setting enable skirt adhesion checkbox text: catalog.i18nc("@option:check", "Print Brim"); style: UM.Theme.styles.checkbox; + enabled: base.settingsEnabled checked: platformAdhesionType.properties.value == "brim" @@ -212,6 +240,7 @@ Item id: brimMouseArea anchors.fill: parent hoverEnabled: true + enabled: base.settingsEnabled onClicked: { platformAdhesionType.setPropertyValue("value", !parent.checked ? "brim" : "skirt") @@ -253,12 +282,14 @@ Item //: Setting enable support checkbox text: catalog.i18nc("@option:check", "Print Support Structure"); style: UM.Theme.styles.checkbox; + enabled: base.settingsEnabled checked: supportEnabled.properties.value == "True" MouseArea { id: supportMouseArea anchors.fill: parent hoverEnabled: true + enabled: base.settingsEnabled onClicked: { supportEnabled.setPropertyValue("value", !parent.checked) @@ -287,6 +318,7 @@ Item width: parent.width / 100 * 45 style: UM.Theme.styles.combobox + enabled: base.settingsEnabled property alias _hovered: supportExtruderMouseArea.containsMouse currentIndex: supportEnabled.properties.value == "True" ? parseFloat(supportExtruderNr.properties.value) + 1 : 0 @@ -302,6 +334,7 @@ Item id: supportExtruderMouseArea anchors.fill: parent hoverEnabled: true + enabled: base.settingsEnabled acceptedButtons: Qt.NoButton onEntered: { @@ -381,7 +414,7 @@ Item { id: platformAdhesionType - containerStackId: Cura.MachineManager.activeMachineId + containerStackId: Cura.MachineManager.activeStackId key: "adhesion_type" watchedProperties: [ "value" ] storeIndex: 0 @@ -391,7 +424,7 @@ Item { id: supportEnabled - containerStackId: Cura.MachineManager.activeMachineId + containerStackId: Cura.MachineManager.activeStackId key: "support_enable" watchedProperties: [ "value" ] storeIndex: 0 @@ -411,7 +444,7 @@ Item { id: supportExtruderNr - containerStackId: Cura.MachineManager.activeMachineId + containerStackId: Cura.MachineManager.activeStackId key: "support_extruder_nr" watchedProperties: [ "value" ] storeIndex: 0 diff --git a/resources/shaders/striped.shader b/resources/shaders/striped.shader new file mode 100644 index 0000000000..0114f0b2cb --- /dev/null +++ b/resources/shaders/striped.shader @@ -0,0 +1,84 @@ +[shaders] +vertex = + uniform highp mat4 u_modelMatrix; + uniform highp mat4 u_viewProjectionMatrix; + uniform highp mat4 u_normalMatrix; + + attribute highp vec4 a_vertex; + attribute highp vec4 a_normal; + attribute highp vec2 a_uvs; + + varying highp vec3 v_position; + varying highp vec3 v_vertex; + varying highp vec3 v_normal; + + void main() + { + vec4 world_space_vert = u_modelMatrix * a_vertex; + gl_Position = u_viewProjectionMatrix * world_space_vert; + + v_position = gl_Position.xyz; + v_vertex = world_space_vert.xyz; + v_normal = (u_normalMatrix * normalize(a_normal)).xyz; + } + +fragment = + uniform mediump vec4 u_ambientColor; + uniform mediump vec4 u_diffuseColor1; + uniform mediump vec4 u_diffuseColor2; + uniform mediump vec4 u_specularColor; + uniform highp vec3 u_lightPosition; + uniform mediump float u_shininess; + uniform highp vec3 u_viewPosition; + + uniform mediump float u_width; + + varying highp vec3 v_position; + varying highp vec3 v_vertex; + varying highp vec3 v_normal; + + void main() + { + mediump vec4 finalColor = vec4(0.0); + mediump vec4 diffuseColor = (mod((-v_position.x + v_position.y), u_width) < (u_width / 2.)) ? u_diffuseColor1 : u_diffuseColor2; + + /* Ambient Component */ + finalColor += u_ambientColor; + + highp vec3 normal = normalize(v_normal); + highp vec3 lightDir = normalize(u_lightPosition - v_vertex); + + /* Diffuse Component */ + highp float NdotL = clamp(abs(dot(normal, lightDir)), 0.0, 1.0); + finalColor += (NdotL * diffuseColor); + + /* Specular Component */ + /* TODO: We should not do specularity for fragments facing away from the light.*/ + highp vec3 reflectedLight = reflect(-lightDir, normal); + highp vec3 viewVector = normalize(u_viewPosition - v_vertex); + highp float NdotR = clamp(dot(viewVector, reflectedLight), 0.0, 1.0); + finalColor += pow(NdotR, u_shininess) * u_specularColor; + + gl_FragColor = finalColor; + gl_FragColor.a = 1.0; + } + +[defaults] +u_ambientColor = [0.3, 0.3, 0.3, 1.0] +u_diffuseColor1 = [1.0, 0.5, 0.5, 1.0] +u_diffuseColor2 = [0.5, 0.5, 0.5, 1.0] +u_specularColor = [0.4, 0.4, 0.4, 1.0] +u_shininess = 20.0 +u_width = 5.0 + +[bindings] +u_modelMatrix = model_matrix +u_viewProjectionMatrix = view_projection_matrix +u_normalMatrix = normal_matrix +u_viewPosition = view_position +u_lightPosition = light_0_position +u_diffuseColor = diffuse_color + +[attributes] +a_vertex = vertex +a_normal = normal diff --git a/resources/themes/cura/icons/link.svg b/resources/themes/cura/icons/link.svg new file mode 100644 index 0000000000..7cc2778846 --- /dev/null +++ b/resources/themes/cura/icons/link.svg @@ -0,0 +1,43 @@ + +image/svg+xml \ No newline at end of file diff --git a/resources/themes/cura/icons/reset.svg b/resources/themes/cura/icons/reset.svg index fae303d2a1..4772d446d7 100644 --- a/resources/themes/cura/icons/reset.svg +++ b/resources/themes/cura/icons/reset.svg @@ -27,10369 +27,4 @@ c-1.128,0.65-2.448,0.967-3.679,1.439C-0.113,107.552-0.113,69.744-0.113,31.935z"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index 053e5e3d84..69fc2c2c71 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -56,7 +56,7 @@ "text_inactive": [174, 174, 174, 255], "text_hover": [70, 84, 113, 255], "text_pressed": [12, 169, 227, 255], - "text_white": [255, 255, 255, 255], + "text_reversed": [255, 255, 255, 255], "text_subtext": [70, 84, 113, 255], "error": [255, 140, 0, 255],