diff --git a/CMakeLists.txt b/CMakeLists.txt index 96efd68a2f..9e9bf4b538 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,10 @@ endif() set(CURA_VERSION "master" CACHE STRING "Version name of Cura") set(CURA_BUILDTYPE "" CACHE STRING "Build type of Cura, eg. 'PPA'") -set(CURA_PACKAGES_VERSION "" CACHE STRING "Packages version of Cura") +set(CURA_SDK_VERSION "" CACHE STRING "SDK version of Cura") +set(CURA_CLOUD_API_ROOT "" CACHE STRING "Alternative Cura cloud API root") +set(CURA_CLOUD_API_VERSION "" CACHE STRING "Alternative Cura cloud API version") + configure_file(${CMAKE_SOURCE_DIR}/cura.desktop.in ${CMAKE_BINARY_DIR}/cura.desktop @ONLY) configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY) diff --git a/cura/Arranging/Arrange.py b/cura/Arranging/Arrange.py index 1027b39199..21ed45dbf1 100644 --- a/cura/Arranging/Arrange.py +++ b/cura/Arranging/Arrange.py @@ -3,6 +3,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Logger import Logger +from UM.Math.Polygon import Polygon from UM.Math.Vector import Vector from cura.Arranging.ShapeArray import ShapeArray from cura.Scene import ZOffsetDecorator @@ -45,7 +46,7 @@ class Arrange: # \param scene_root Root for finding all scene nodes # \param fixed_nodes Scene nodes to be placed @classmethod - def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5, x = 350, y = 250): + def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5, x = 350, y = 250, min_offset = 8): arranger = Arrange(x, y, x // 2, y // 2, scale = scale) arranger.centerFirst() @@ -58,9 +59,10 @@ class Arrange: # Place all objects fixed nodes for fixed_node in fixed_nodes: - vertices = fixed_node.callDecoration("getConvexHull") + vertices = fixed_node.callDecoration("getConvexHullHead") or fixed_node.callDecoration("getConvexHull") if not vertices: continue + vertices = vertices.getMinkowskiHull(Polygon.approximatedCircle(min_offset)) points = copy.deepcopy(vertices._points) shape_arr = ShapeArray.fromPolygon(points, scale = scale) arranger.place(0, 0, shape_arr) @@ -81,12 +83,12 @@ class Arrange: ## Find placement for a node (using offset shape) and place it (using hull shape) # return the nodes that should be placed # \param node - # \param offset_shape_arr ShapeArray with offset, used to find location - # \param hull_shape_arr ShapeArray without offset, for placing the shape + # \param offset_shape_arr ShapeArray with offset, for placing the shape + # \param hull_shape_arr ShapeArray without offset, used to find location def findNodePlacement(self, node, offset_shape_arr, hull_shape_arr, step = 1): new_node = copy.deepcopy(node) best_spot = self.bestSpot( - offset_shape_arr, start_prio = self._last_priority, step = step) + hull_shape_arr, start_prio = self._last_priority, step = step) x, y = best_spot.x, best_spot.y # Save the last priority. @@ -102,7 +104,7 @@ class Arrange: if x is not None: # We could find a place new_node.setPosition(Vector(x, center_y, y)) found_spot = True - self.place(x, y, hull_shape_arr) # place the object in arranger + self.place(x, y, offset_shape_arr) # place the object in arranger else: Logger.log("d", "Could not find spot!"), found_spot = False diff --git a/cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py b/cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py index 252cef4e65..6abc553a4b 100644 --- a/cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py +++ b/cura/Arranging/ArrangeObjectsAllBuildPlatesJob.py @@ -110,7 +110,7 @@ class ArrangeObjectsAllBuildPlatesJob(Job): arrange_array.add() arranger = arrange_array.get(current_build_plate_number) - best_spot = arranger.bestSpot(offset_shape_arr, start_prio=start_priority) + best_spot = arranger.bestSpot(hull_shape_arr, start_prio=start_priority) x, y = best_spot.x, best_spot.y node.removeDecorator(ZOffsetDecorator) if node.getBoundingBox(): @@ -118,7 +118,7 @@ class ArrangeObjectsAllBuildPlatesJob(Job): else: center_y = 0 if x is not None: # We could find a place - arranger.place(x, y, hull_shape_arr) # place the object in the arranger + arranger.place(x, y, offset_shape_arr) # place the object in the arranger node.callDecoration("setBuildPlateNumber", current_build_plate_number) grouped_operation.addOperation(TranslateOperation(node, Vector(x, center_y, y), set_position = True)) diff --git a/cura/Arranging/ArrangeObjectsJob.py b/cura/Arranging/ArrangeObjectsJob.py index 01a91a3c22..5e982582fd 100644 --- a/cura/Arranging/ArrangeObjectsJob.py +++ b/cura/Arranging/ArrangeObjectsJob.py @@ -37,12 +37,15 @@ class ArrangeObjectsJob(Job): machine_width = global_container_stack.getProperty("machine_width", "value") machine_depth = global_container_stack.getProperty("machine_depth", "value") - arranger = Arrange.create(x = machine_width, y = machine_depth, fixed_nodes = self._fixed_nodes) + arranger = Arrange.create(x = machine_width, y = machine_depth, fixed_nodes = self._fixed_nodes, min_offset = self._min_offset) # Collect nodes to be placed nodes_arr = [] # fill with (size, node, offset_shape_arr, hull_shape_arr) for node in self._nodes: offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = self._min_offset) + if offset_shape_arr is None: + Logger.log("w", "Node [%s] could not be converted to an array for arranging...", str(node)) + continue nodes_arr.append((offset_shape_arr.arr.shape[0] * offset_shape_arr.arr.shape[1], node, offset_shape_arr, hull_shape_arr)) # Sort the nodes with the biggest area first. @@ -63,7 +66,7 @@ class ArrangeObjectsJob(Job): start_priority = last_priority else: start_priority = 0 - best_spot = arranger.bestSpot(offset_shape_arr, start_prio=start_priority) + best_spot = arranger.bestSpot(hull_shape_arr, start_prio = start_priority) x, y = best_spot.x, best_spot.y node.removeDecorator(ZOffsetDecorator) if node.getBoundingBox(): @@ -74,7 +77,7 @@ class ArrangeObjectsJob(Job): last_size = size last_priority = best_spot.priority - arranger.place(x, y, hull_shape_arr) # take place before the next one + arranger.place(x, y, offset_shape_arr) # take place before the next one grouped_operation.addOperation(TranslateOperation(node, Vector(x, center_y, y), set_position = True)) else: Logger.log("d", "Arrange all: could not find spot!") diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 5db616ab3b..e416196775 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -452,7 +452,7 @@ class CuraApplication(QtApplication): ## A reusable dialogbox # - showMessageBox = pyqtSignal(str, str, str, str, str, int, int, arguments = ["title", "footer", "text", "informativeText", "detailedText", "buttons", "icon"]) + showMessageBox = pyqtSignal(str, str, str, str, int, int, arguments = ["title", "text", "informativeText", "detailedText", "buttons", "icon"]) def messageBox(self, title, text, informativeText = "", detailedText = "", buttons = QMessageBox.Ok, icon = QMessageBox.NoIcon, callback = None, callback_arguments = []): self._message_box_callback = callback @@ -1500,11 +1500,15 @@ 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! - mesh_data = job.getResult()[0].getMeshData() - if mesh_data: - job._node.setMeshData(mesh_data) - else: + job_result = job.getResult() + if len(job_result) == 0: + Logger.log("e", "Reloading the mesh failed.") + return + mesh_data = job_result[0].getMeshData() + if not mesh_data: Logger.log("w", "Could not find a mesh in reloaded node.") + return + job._node.setMeshData(mesh_data) def _openFile(self, filename): self.readLocalFile(QUrl.fromLocalFile(filename)) @@ -1568,10 +1572,11 @@ class CuraApplication(QtApplication): f = file.toLocalFile() extension = os.path.splitext(f)[1] + extension = extension.lower() filename = os.path.basename(f) if len(self._currently_loading_files) > 0: # If a non-slicable file is already being loaded, we prevent loading of any further non-slicable files - if extension.lower() in self._non_sliceable_extensions: + if extension in self._non_sliceable_extensions: message = Message( self._i18n_catalog.i18nc("@info:status", "Only one G-code file can be loaded at a time. Skipped importing {0}", @@ -1580,7 +1585,8 @@ class CuraApplication(QtApplication): return # If file being loaded is non-slicable file, then prevent loading of any other files extension = os.path.splitext(self._currently_loading_files[0])[1] - if extension.lower() in self._non_sliceable_extensions: + extension = extension.lower() + if extension in self._non_sliceable_extensions: message = Message( self._i18n_catalog.i18nc("@info:status", "Can't open any other file if G-code is loading. Skipped importing {0}", diff --git a/cura/CuraPackageManager.py b/cura/CuraPackageManager.py index bc2e020037..096bfc9065 100644 --- a/cura/CuraPackageManager.py +++ b/cura/CuraPackageManager.py @@ -158,14 +158,17 @@ class CuraPackageManager(QObject): # Add bundled plugins if package_id in self._bundled_package_dict: package_info = self._bundled_package_dict[package_id]["package_info"] + package_info["is_installed"] = True # Add installed plugins if package_id in self._installed_package_dict: package_info = self._installed_package_dict[package_id]["package_info"] + package_info["is_installed"] = True # Add to install plugins if package_id in self._to_install_package_dict: package_info = self._to_install_package_dict[package_id]["package_info"] + package_info["is_installed"] = False if package_info is None: continue diff --git a/cura/CuraVersion.py.in b/cura/CuraVersion.py.in index f45a24cae9..226b2183f2 100644 --- a/cura/CuraVersion.py.in +++ b/cura/CuraVersion.py.in @@ -4,4 +4,6 @@ CuraVersion = "@CURA_VERSION@" CuraBuildType = "@CURA_BUILDTYPE@" CuraDebugMode = True if "@_cura_debugmode@" == "ON" else False -CuraPackagesVersion = "@CURA_PACKAGES_VERSION@" +CuraSDKVersion = "@CURA_SDK_VERSION@" +CuraCloudAPIRoot = "@CURA_CLOUD_API_ROOT@" +CuraCloudAPIVersion = "@CURA_CLOUD_API_VERSION@" diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index 8b74596667..ff666f392d 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -291,9 +291,10 @@ class MaterialManager(QObject): material_id_metadata_dict = dict() for node in nodes_to_check: if node is not None: + # Only exclude the materials that are explicitly specified in the "exclude_materials" field. + # Do not exclude other materials that are of the same type. for material_id, node in node.material_map.items(): - fallback_id = self.getFallbackMaterialIdByMaterialType(node.metadata["material"]) - if fallback_id in machine_exclude_materials: + if material_id in machine_exclude_materials: Logger.log("d", "Exclude material [%s] for machine [%s]", material_id, machine_definition.getId()) continue diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py index 0a1337feeb..4759c8b5b0 100644 --- a/cura/Machines/Models/BaseMaterialsModel.py +++ b/cura/Machines/Models/BaseMaterialsModel.py @@ -39,6 +39,8 @@ class BaseMaterialsModel(ListModel): self._extruder_position = 0 self._extruder_stack = None + # Update the stack and the model data when the machine changes + self._machine_manager.globalContainerChanged.connect(self._updateExtruderStack) def _updateExtruderStack(self): global_stack = self._machine_manager.activeMachine @@ -50,9 +52,11 @@ class BaseMaterialsModel(ListModel): self._extruder_stack = global_stack.extruders.get(str(self._extruder_position)) if self._extruder_stack is not None: self._extruder_stack.pyqtContainersChanged.connect(self._update) + # Force update the model when the extruder stack changes + self._update() def setExtruderPosition(self, position: int): - if self._extruder_position != position: + if self._extruder_stack is None or self._extruder_position != position: self._extruder_position = position self._updateExtruderStack() self.extruderPositionChanged.emit() diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index 46f7f56f8a..57db7734e7 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -1,6 +1,8 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import copy + from UM.Job import Job from UM.Operations.GroupedOperation import GroupedOperation from UM.Message import Message @@ -36,7 +38,7 @@ class MultiplyObjectsJob(Job): root = scene.getRoot() scale = 0.5 - arranger = Arrange.create(x = machine_width, y = machine_depth, scene_root = root, scale = scale) + arranger = Arrange.create(x = machine_width, y = machine_depth, scene_root = root, scale = scale, min_offset = self._min_offset) processed_nodes = [] nodes = [] @@ -64,6 +66,8 @@ class MultiplyObjectsJob(Job): # We do place the nodes one by one, as we want to yield in between. if not node_too_big: new_node, solution_found = arranger.findNodePlacement(current_node, offset_shape_arr, hull_shape_arr) + else: + new_node = copy.deepcopy(node) if node_too_big or not solution_found: found_solution_for_all = False new_location = new_node.getPosition() diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 2c9f6511fa..d17720228f 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -329,6 +329,8 @@ class PrintInformation(QObject): baseNameChanged = pyqtSignal() def setBaseName(self, base_name: str, is_project_file: bool = False): + self._is_user_specified_job_name = False + # Ensure that we don't use entire path but only filename name = os.path.basename(base_name) @@ -355,11 +357,12 @@ class PrintInformation(QObject): data = mime_type.stripExtension(name) except: Logger.log("w", "Unsupported Mime Type Database file extension") + data = 'unnamed' if data is not None and check_name is not None: self._base_name = data else: - self._base_name = '' + self._base_name = 'unnamed' self._updateJobName() diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 6d8ed7c037..f5036078be 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -475,7 +475,7 @@ class CuraContainerRegistry(ContainerRegistry): extruder_definition = extruder_definitions[0] unique_name = self.uniqueName(machine.getName() + " " + new_extruder_id) if create_new_ids else machine.getName() + " " + new_extruder_id - extruder_stack = ExtruderStack.ExtruderStack(unique_name, parent = machine) + extruder_stack = ExtruderStack.ExtruderStack(unique_name) extruder_stack.setName(extruder_definition.getName()) extruder_stack.setDefinition(extruder_definition) extruder_stack.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position")) diff --git a/cura/Settings/CuraContainerStack.py b/cura/Settings/CuraContainerStack.py index 308a91bc76..e1f89eb725 100755 --- a/cura/Settings/CuraContainerStack.py +++ b/cura/Settings/CuraContainerStack.py @@ -39,8 +39,8 @@ from . import Exceptions # This also means that operations on the stack that modifies the container ordering is prohibited and # will raise an exception. class CuraContainerStack(ContainerStack): - def __init__(self, container_id: str, *args, **kwargs): - super().__init__(container_id, *args, **kwargs) + def __init__(self, container_id: str): + super().__init__(container_id) self._container_registry = ContainerRegistry.getInstance() diff --git a/cura/Settings/CuraStackBuilder.py b/cura/Settings/CuraStackBuilder.py index 640489adb3..da8315b3d2 100644 --- a/cura/Settings/CuraStackBuilder.py +++ b/cura/Settings/CuraStackBuilder.py @@ -99,8 +99,7 @@ class CuraStackBuilder: position = position, variant_container = extruder_variant_container, material_container = material_container, - quality_container = application.empty_quality_container, - global_stack = new_global_stack, + quality_container = application.empty_quality_container ) new_extruder.setNextStack(new_global_stack) new_global_stack.addExtruder(new_extruder) @@ -139,11 +138,11 @@ class CuraStackBuilder: @classmethod def createExtruderStack(cls, new_stack_id: str, extruder_definition: DefinitionContainerInterface, machine_definition_id: str, position: int, - variant_container, material_container, quality_container, global_stack) -> ExtruderStack: + variant_container, material_container, quality_container) -> ExtruderStack: from cura.CuraApplication import CuraApplication application = CuraApplication.getInstance() - stack = ExtruderStack(new_stack_id, parent = global_stack) + stack = ExtruderStack(new_stack_id) stack.setName(extruder_definition.getName()) stack.setDefinition(extruder_definition) diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index 5e944b401f..b3f7d529a2 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -25,8 +25,8 @@ if TYPE_CHECKING: # # class ExtruderStack(CuraContainerStack): - def __init__(self, container_id: str, *args, **kwargs): - super().__init__(container_id, *args, **kwargs) + def __init__(self, container_id: str): + super().__init__(container_id) self.addMetaDataEntry("type", "extruder_train") # For backward compatibility diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index f76ac1ab3f..6d300954c2 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -23,8 +23,8 @@ from .CuraContainerStack import CuraContainerStack ## Represents the Global or Machine stack and its related containers. # class GlobalStack(CuraContainerStack): - def __init__(self, container_id: str, *args, **kwargs): - super().__init__(container_id, *args, **kwargs) + def __init__(self, container_id: str): + super().__init__(container_id) self.addMetaDataEntry("type", "machine") # For backward compatibility diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 1b7aef65d6..389c4e4de3 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1041,6 +1041,10 @@ class MachineManager(QObject): self.activeQualityChangesGroupChanged.emit() def _setQualityGroup(self, quality_group, empty_quality_changes: bool = True) -> None: + if quality_group is None: + self._setEmptyQuality() + return + if quality_group.node_for_global.getContainer() is None: return for node in quality_group.nodes_for_extruders.values(): @@ -1051,10 +1055,6 @@ class MachineManager(QObject): if empty_quality_changes: self._current_quality_changes_group = None - if quality_group is None: - self._setEmptyQuality() - return - # Set quality and quality_changes for the GlobalStack self._global_container_stack.quality = quality_group.node_for_global.getContainer() if empty_quality_changes: @@ -1289,6 +1289,10 @@ class MachineManager(QObject): self._global_container_stack.variant = self._empty_variant_container self._updateQualityWithMaterial() + # See if we need to show the Discard or Keep changes screen + if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1: + self._application.discardOrKeepProfileChanges() + ## Find all container stacks that has the pair 'key = value' in its metadata and replaces the value with 'new_value' def replaceContainersMetadata(self, key: str, value: str, new_value: str) -> None: machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine") @@ -1340,6 +1344,10 @@ class MachineManager(QObject): self._setMaterial(position, container_node) self._updateQualityWithMaterial() + # See if we need to show the Discard or Keep changes screen + if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1: + self._application.discardOrKeepProfileChanges() + @pyqtSlot(str, str) def setVariantByName(self, position: str, variant_name: str) -> None: machine_definition_id = self._global_container_stack.definition.id @@ -1355,6 +1363,10 @@ class MachineManager(QObject): self._updateMaterialWithVariant(position) self._updateQualityWithMaterial() + # See if we need to show the Discard or Keep changes screen + if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1: + self._application.discardOrKeepProfileChanges() + @pyqtSlot(str) def setQualityGroupByQualityType(self, quality_type: str) -> None: if self._global_container_stack is None: diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 9eec09b202..6c2fb9a59d 100755 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -15,7 +15,6 @@ from UM.Math.Vector import Vector from UM.Mesh.MeshBuilder import MeshBuilder from UM.Mesh.MeshReader import MeshReader from UM.Scene.GroupDecorator import GroupDecorator -from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType from cura.Settings.ExtruderManager import ExtruderManager from cura.Scene.CuraSceneNode import CuraSceneNode @@ -26,15 +25,6 @@ from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch MYPY = False - -MimeTypeDatabase.addMimeType( - MimeType( - name = "application/x-cura-project-file", - comment = "Cura Project File", - suffixes = ["curaproject.3mf"] - ) -) - try: if not MYPY: import xml.etree.cElementTree as ET diff --git a/plugins/3MFReader/__init__.py b/plugins/3MFReader/__init__.py index 9da54586a3..304cd8ef65 100644 --- a/plugins/3MFReader/__init__.py +++ b/plugins/3MFReader/__init__.py @@ -13,8 +13,24 @@ from . import ThreeMFWorkspaceReader from UM.i18n import i18nCatalog from UM.Platform import Platform +from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType catalog = i18nCatalog("cura") +MimeTypeDatabase.addMimeType( + MimeType( + name = "application/x-cura-project-file", + comment = "Cura Project File", + suffixes = ["curaproject.3mf"] + ) +) +MimeTypeDatabase.addMimeType( + MimeType( + name = "application/x-cura-project-file", + comment = "Cura Project File", + suffixes = ["3mf"] + ) +) + def getMetaData() -> Dict: # Workarround for osx not supporting double file extensions correctly. if Platform.isOSX(): diff --git a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py index 19b77c1181..ce4fb8c92f 100644 --- a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py +++ b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py @@ -64,20 +64,25 @@ class FirmwareUpdateCheckerJob(Job): if (checked_version != "") and (checked_version != current_version): Logger.log("i", "SHOWING FIRMWARE UPDATE MESSAGE") - footer_text = i18n_catalog.i18nc("@action:info", "Read more on how to update printer firmware") - footer_link = "?url=https://ultimaker.com/en/resources/23129-updating-the-firmware?utm_source=cura&utm_medium=software&utm_campaign=hw-update" - - footer_message = footer_text + " " + footer_link - message = Message(i18n_catalog.i18nc( "@info Don't translate {machine_name}, since it gets replaced by a printer name!", "New features are available for your {machine_name}! It is recommended to update the firmware on your printer.").format( machine_name=machine_name), title=i18n_catalog.i18nc( "@info:title The %s gets replaced with the printer name.", - "New %s firmware available") % machine_name, - footer = footer_message) + "New %s firmware available") % machine_name) + message.addAction("download", + i18n_catalog.i18nc("@action:button", "How to update"), + "[no_icon]", + "[no_description]", + button_style=Message.ActionButtonStyle.LINK, + button_align=Message.ActionButtonStyle.BUTTON_ALIGN_LEFT) + + + # If we do this in a cool way, the download url should be available in the JSON file + if self._set_download_url_callback: + self._set_download_url_callback("https://ultimaker.com/en/resources/20500-upgrade-firmware") message.actionTriggered.connect(self._callback) message.show() diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 82e07da464..b0525bdbfa 100755 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -46,10 +46,11 @@ class SliceInfo(QObject, Extension): dismissable = False, title = catalog.i18nc("@info:title", "Collecting Data")) - self.send_slice_info_message.addAction("Dismiss", name = catalog.i18nc("@action:button", "Allow"), icon = None, - description = catalog.i18nc("@action:tooltip", "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing.")) self.send_slice_info_message.addAction("MoreInfo", name = catalog.i18nc("@action:button", "More info"), icon = None, description = catalog.i18nc("@action:tooltip", "See more information on what data Cura sends."), button_style = Message.ActionButtonStyle.LINK) + + self.send_slice_info_message.addAction("Dismiss", name = catalog.i18nc("@action:button", "Allow"), icon = None, + description = catalog.i18nc("@action:tooltip", "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing.")) self.send_slice_info_message.actionTriggered.connect(self.messageActionTriggered) self.send_slice_info_message.show() diff --git a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml index 229ab5afb3..b4219d53bf 100644 --- a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml +++ b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml @@ -31,8 +31,9 @@ Item frameVisible: false selectionMode: 0 model: packageData.supported_configs - headerDelegate: Item + headerDelegate: Rectangle { + color: UM.Theme.getColor("sidebar") height: UM.Theme.getSize("toolbox_chart_row").height Label { diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml index e1ffc6326c..05186b961d 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml @@ -12,6 +12,7 @@ Column height: childrenRect.height width: parent.width spacing: UM.Theme.getSize("default_margin").height + /* Hidden for 3.4 Label { id: heading @@ -20,6 +21,7 @@ Column color: UM.Theme.getColor("text_medium") font: UM.Theme.getFont("medium") } + */ GridLayout { id: grid diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml index 170fd10fc7..69e508cd55 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml @@ -18,6 +18,8 @@ ScrollView spacing: UM.Theme.getSize("default_margin").height padding: UM.Theme.getSize("wide_margin").height height: childrenRect.height + 2 * padding + + /* Hide for 3.4 ToolboxDownloadsShowcase { id: showcase @@ -29,6 +31,8 @@ ScrollView width: parent.width height: UM.Theme.getSize("default_lining").height } + */ + ToolboxDownloadsGrid { id: allPlugins diff --git a/plugins/Toolbox/resources/qml/ToolboxHeader.qml b/plugins/Toolbox/resources/qml/ToolboxHeader.qml index 88495e3f63..ee4241beaf 100644 --- a/plugins/Toolbox/resources/qml/ToolboxHeader.qml +++ b/plugins/Toolbox/resources/qml/ToolboxHeader.qml @@ -33,6 +33,8 @@ Item toolbox.viewPage = "overview" } } + + /* Hide for 3.4 ToolboxTabButton { text: catalog.i18nc("@title:tab", "Materials") @@ -45,6 +47,7 @@ Item toolbox.viewPage = "overview" } } + */ } ToolboxTabButton { diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml index 9d916182f6..bb0f6fe346 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml @@ -65,6 +65,7 @@ ScrollView } } } + /* Hidden in 3.4 Label { visible: toolbox.materialsInstalledModel.items.length > 0 @@ -102,5 +103,6 @@ ScrollView } } } + */ } } diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml index 78c970659c..b16564fdd2 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml @@ -16,7 +16,7 @@ Item { color: UM.Theme.getColor("lining") width: parent.width - height: UM.Theme.getSize("default_lining").height + height: Math.floor(UM.Theme.getSize("default_lining").height) anchors.bottom: parent.bottom } Row @@ -40,14 +40,14 @@ Item Column { id: pluginInfo - topPadding: UM.Theme.getSize("default_margin").height / 2 + topPadding: Math.floor(UM.Theme.getSize("default_margin").height / 2) property var color: model.type === "plugin" && !isEnabled ? UM.Theme.getColor("lining") : UM.Theme.getColor("text") - width: tileRow.width - (authorInfo.width + pluginActions.width + 2 * tileRow.spacing + ((disableButton.visible) ? disableButton.width + tileRow.spacing : 0)) + width: Math.floor(tileRow.width - (authorInfo.width + pluginActions.width + 2 * tileRow.spacing + ((disableButton.visible) ? disableButton.width + tileRow.spacing : 0))) Label { text: model.name width: parent.width - height: UM.Theme.getSize("toolbox_property_label").height + height: Math.floor(UM.Theme.getSize("toolbox_property_label").height) wrapMode: Text.WordWrap font: UM.Theme.getFont("default_bold") color: pluginInfo.color @@ -81,7 +81,7 @@ Item } } width: parent.width - height: UM.Theme.getSize("toolbox_property_label").height + height: Math.floor(UM.Theme.getSize("toolbox_property_label").height) wrapMode: Text.WordWrap verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignLeft diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml index 0ae738b71d..b0aecfc9a2 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml @@ -13,6 +13,16 @@ Column width: UM.Theme.getSize("toolbox_action_button").width spacing: UM.Theme.getSize("narrow_margin").height + Label + { + visible: !model.is_installed + text: catalog.i18nc("@label", "Will install upon restarting") + color: UM.Theme.getColor("lining") + font: UM.Theme.getFont("default") + wrapMode: Text.WordWrap + width: parent.width + } + ToolboxProgressButton { id: updateButton @@ -39,7 +49,7 @@ Column { id: removeButton text: canDowngrade ? catalog.i18nc("@action:button", "Downgrade") : catalog.i18nc("@action:button", "Uninstall") - visible: !model.is_bundled + visible: !model.is_bundled && model.is_installed enabled: !toolbox.isDownloading style: ButtonStyle { diff --git a/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml b/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml index b598bd96d0..2744e40ec9 100644 --- a/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml +++ b/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml @@ -150,7 +150,7 @@ Item { id: loader visible: active - source: "../images/loading.gif" + source: visible ? "../images/loading.gif" : "" width: UM.Theme.getSize("toolbox_loader").width height: UM.Theme.getSize("toolbox_loader").height anchors.right: button.left diff --git a/plugins/Toolbox/src/PackagesModel.py b/plugins/Toolbox/src/PackagesModel.py index 5ec3eba1d8..2a3b169e87 100644 --- a/plugins/Toolbox/src/PackagesModel.py +++ b/plugins/Toolbox/src/PackagesModel.py @@ -29,8 +29,9 @@ class PackagesModel(ListModel): self.addRoleName(Qt.UserRole + 12, "last_updated") self.addRoleName(Qt.UserRole + 13, "is_bundled") self.addRoleName(Qt.UserRole + 14, "is_enabled") - self.addRoleName(Qt.UserRole + 15, "has_configs") - self.addRoleName(Qt.UserRole + 16, "supported_configs") + self.addRoleName(Qt.UserRole + 15, "is_installed") # Scheduled pkgs are included in the model but should not be marked as actually installed + self.addRoleName(Qt.UserRole + 16, "has_configs") + self.addRoleName(Qt.UserRole + 17, "supported_configs") # List of filters for queries. The result is the union of the each list of results. self._filter = {} # type: Dict[str, str] @@ -73,6 +74,7 @@ class PackagesModel(ListModel): "last_updated": package["last_updated"] if "last_updated" in package else None, "is_bundled": package["is_bundled"] if "is_bundled" in package else False, "is_enabled": package["is_enabled"] if "is_enabled" in package else False, + "is_installed": package["is_installed"] if "is_installed" in package else False, "has_configs": has_configs, "supported_configs": configs_model }) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index e540bce523..b019dfccbe 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -28,7 +28,8 @@ i18n_catalog = i18nCatalog("cura") ## The Toolbox class is responsible of communicating with the server through the API class Toolbox(QObject, Extension): - DEFAULT_PACKAGES_API_ROOT = "https://api.ultimaker.com" + DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" + DEFAULT_CLOUD_API_VERSION = 1 def __init__(self, parent=None) -> None: super().__init__(parent) @@ -37,16 +38,10 @@ class Toolbox(QObject, Extension): self._package_manager = None self._plugin_registry = Application.getInstance().getPluginRegistry() - self._sdk_version = self._getPackagesVersion() - - self._cloud_api_version = 1 - self._cloud_api_root = self._getPackagesApiRoot() - - self._api_url = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format( - cloud_api_root = self._cloud_api_root, - cloud_api_version = self._cloud_api_version, - sdk_version = self._sdk_version - ) + self._sdk_version = None + self._cloud_api_version = None + self._cloud_api_root = None + self._api_url = None # Network: self._get_packages_request = None @@ -67,12 +62,7 @@ class Toolbox(QObject, Extension): ) ) ] - self._request_urls = { - "authors": QUrl("{base_url}/authors".format(base_url = self._api_url)), - "packages": QUrl("{base_url}/packages".format(base_url = self._api_url)), - "plugins_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url)), - "materials_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url)) - } + self._request_urls = {} self._to_update = [] # Package_ids that are waiting to be updated # Data: @@ -164,22 +154,50 @@ class Toolbox(QObject, Extension): # this is initialized. Therefore, we wait until the application is ready. def _onAppInitialized(self) -> None: self._package_manager = Application.getInstance().getCuraPackageManager() + self._sdk_version = self._getSDKVersion() + self._cloud_api_version = self._getCloudAPIVersion() + self._cloud_api_root = self._getCloudAPIRoot() + self._api_url = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format( + cloud_api_root=self._cloud_api_root, + cloud_api_version=self._cloud_api_version, + sdk_version=self._sdk_version + ) + self._request_urls = { + "authors": QUrl("{base_url}/authors".format(base_url=self._api_url)), + "packages": QUrl("{base_url}/packages".format(base_url=self._api_url)), + "plugins_showcase": QUrl("{base_url}/showcase".format(base_url=self._api_url)), + "materials_showcase": QUrl("{base_url}/showcase".format(base_url=self._api_url)) + } # Get the API root for the packages API depending on Cura version settings. - def _getPackagesApiRoot(self) -> str: + def _getCloudAPIRoot(self) -> str: if not hasattr(cura, "CuraVersion"): - return self.DEFAULT_PACKAGES_API_ROOT - if not hasattr(cura.CuraVersion, "CuraPackagesApiRoot"): - return self.DEFAULT_PACKAGES_API_ROOT - return cura.CuraVersion.CuraPackagesApiRoot + return self.DEFAULT_CLOUD_API_ROOT + if not hasattr(cura.CuraVersion, "CuraCloudAPIRoot"): + return self.DEFAULT_CLOUD_API_ROOT + if not cura.CuraVersion.CuraCloudAPIRoot: + return self.DEFAULT_CLOUD_API_ROOT + return cura.CuraVersion.CuraCloudAPIRoot + + # Get the cloud API version from CuraVersion + def _getCloudAPIVersion(self) -> int: + if not hasattr(cura, "CuraVersion"): + return self.DEFAULT_CLOUD_API_VERSION + if not hasattr(cura.CuraVersion, "CuraCloudAPIVersion"): + return self.DEFAULT_CLOUD_API_VERSION + if not cura.CuraVersion.CuraCloudAPIVersion: + return self.DEFAULT_CLOUD_API_VERSION + return cura.CuraVersion.CuraCloudAPIVersion # Get the packages version depending on Cura version settings. - def _getPackagesVersion(self) -> int: + def _getSDKVersion(self) -> int: if not hasattr(cura, "CuraVersion"): return self._plugin_registry.APIVersion - if not hasattr(cura.CuraVersion, "CuraPackagesVersion"): + if not hasattr(cura.CuraVersion, "CuraSDKVersion"): return self._plugin_registry.APIVersion - return cura.CuraVersion.CuraPackagesVersion + if not cura.CuraVersion.CuraSDKVersion: + return self._plugin_registry.APIVersion + return cura.CuraVersion.CuraSDKVersion @pyqtSlot() def browsePackages(self) -> None: diff --git a/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py b/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py index 17abace547..e2241fd195 100644 --- a/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py +++ b/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py @@ -6,6 +6,9 @@ import io #To serialise the preference files afterwards. from UM.VersionUpgrade import VersionUpgrade #We're inheriting from this. +_renamed_settings = { + "infill_hollow": "infill_support_enabled" +} ## Upgrades configurations from the state they were in at version 3.3 to the # state they should be in at version 3.4. @@ -38,6 +41,17 @@ class VersionUpgrade33to34(VersionUpgrade): # Update version number. parser["general"]["version"] = "4" + if "values" in parser: + #If infill_hollow was enabled and the overhang angle was adjusted, copy that overhang angle to the new infill support angle. + if "infill_hollow" in parser["values"] and parser["values"]["infill_hollow"] and "support_angle" in parser["values"]: + parser["values"]["infill_support_angle"] = parser["values"]["support_angle"] + + #Renamed settings. + for original, replacement in _renamed_settings.items(): + if original in parser["values"]: + parser["values"][replacement] = parser["values"][original] + del parser["values"][original] + result = io.StringIO() parser.write(result) - return [filename], [result.getvalue()] + return [filename], [result.getvalue()] \ No newline at end of file diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 570b6c8625..25fe841ee4 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1807,6 +1807,30 @@ "limit_to_extruder": "infill_extruder_nr", "settable_per_mesh": true }, + "infill_support_enabled": + { + "label": "Infill Support", + "description": "Print infill structures only where tops of the model should be supported. Enabling this reduces print time and material usage, but leads to ununiform object strength.", + "type": "bool", + "default_value": false, + "enabled": "infill_sparse_density > 0", + "limit_to_extruder": "infill_extruder_nr", + "settable_per_mesh": true + }, + "infill_support_angle": + { + "label": "Infill Overhang Angle", + "description": "The minimum angle of internal overhangs for which infill is added. At a value of 0° objects are totally filled with infill, 90° will not provide any infill.", + "unit": "°", + "type": "float", + "minimum_value": "0", + "minimum_value_warning": "2", + "maximum_value": "90", + "default_value": 40, + "enabled": "infill_sparse_density > 0 and infill_support_enabled", + "limit_to_extruder": "infill_extruder_nr", + "settable_per_mesh": true + }, "skin_preshrink": { "label": "Skin Removal Width", @@ -5788,7 +5812,7 @@ "description": "The file location of an image of which the brightness values determine the minimal density at the corresponding location in the support.", "type": "str", "default_value": "", - "enabled": "infill_pattern == 'cross' or infill_pattern == 'cross_3d'", + "enabled": "support_pattern == 'cross' or support_pattern == 'cross_3d'", "limit_to_extruder": "support_infill_extruder_nr", "settable_per_mesh": false, "settable_per_extruder": true @@ -5918,14 +5942,6 @@ "limit_to_extruder": "support_infill_extruder_nr", "settable_per_mesh": true }, - "infill_hollow": - { - "label": "Hollow Out Objects", - "description": "Remove all infill and make the inside of the object eligible for support.", - "type": "bool", - "default_value": false, - "settable_per_mesh": true - }, "magic_fuzzy_skin_enabled": { "label": "Fuzzy Skin", @@ -6662,14 +6678,6 @@ "type": "float", "enabled": "bridge_settings_enabled and bridge_enable_more_layers", "settable_per_mesh": true - }, - "wall_try_line_thickness": - { - "label": "Try Multiple Line Thicknesses", - "description": "When creating inner walls, try various line thicknesses to fit the wall lines better in narrow spaces. This reduces or increases the inner wall line width by up to 0.01mm.", - "default_value": false, - "type": "bool", - "settable_per_mesh": true } } }, diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index e1cc4f6e45..dce106e219 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -323,10 +323,11 @@ UM.MainWindow { if (drop.urls.length > 0) { - // As the drop area also supports plugins, first check if it's a plugin that was dropped. - if (drop.urls.length == 1) + + var nonPackages = []; + for (var i = 0; i < drop.urls.length; i++) { - var filename = drop.urls[0]; + var filename = drop.urls[i]; if (filename.endsWith(".curapackage")) { // Try to install plugin & close. @@ -334,11 +335,13 @@ UM.MainWindow packageInstallDialog.text = catalog.i18nc("@label", "This package will be installed after restarting."); packageInstallDialog.icon = StandardIcon.Information; packageInstallDialog.open(); - return; + } + else + { + nonPackages.push(filename); } } - - openDialog.handleOpenFileUrls(drop.urls); + openDialog.handleOpenFileUrls(nonPackages); } } } diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index f9ce286706..6cd9999db7 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -81,10 +81,9 @@ Item { text: PrintInformation.jobName horizontalAlignment: TextInput.AlignRight onEditingFinished: { - PrintInformation.setJobName(text, true); - if (printJobTextfield.text != ''){ - printJobTextfield.focus = false; - } + text = text == "" ? "unnamed" : text; + PrintInformation.setJobName(printJobTextfield.text, true); + printJobTextfield.focus = false; } validator: RegExpValidator { regExp: /^[^\\ \/ \*\?\|\[\]]*$/ diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index be8c8bcb45..6f0130d5ca 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -92,6 +92,7 @@ Rectangle anchors.verticalCenter: buildplateIcon.verticalCenter anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").height / 2) text: configuration.buildplateConfiguration + renderType: Text.NativeRendering color: textColor } } diff --git a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml index ca1b666e69..97b5bee745 100644 --- a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml @@ -26,6 +26,7 @@ Column { id: extruderLabel text: catalog.i18nc("@label:extruder label", "Extruder") + renderType: Text.NativeRendering elide: Text.ElideRight anchors.left: parent.left font: UM.Theme.getFont("default") @@ -59,6 +60,7 @@ Column id: extruderNumberText anchors.centerIn: parent text: printCoreConfiguration.position + 1 + renderType: Text.NativeRendering font: UM.Theme.getFont("default") color: mainColor } @@ -69,6 +71,7 @@ Column { id: materialLabel text: printCoreConfiguration.material.name + renderType: Text.NativeRendering elide: Text.ElideRight width: parent.width font: UM.Theme.getFont("default_bold") @@ -79,6 +82,7 @@ Column { id: printCoreTypeLabel text: printCoreConfiguration.hotendID + renderType: Text.NativeRendering elide: Text.ElideRight width: parent.width font: UM.Theme.getFont("default") diff --git a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml b/resources/qml/Menus/ConfigurationMenu/SyncButton.qml index 078acb65b2..3099d684c1 100644 --- a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml +++ b/resources/qml/Menus/ConfigurationMenu/SyncButton.qml @@ -13,7 +13,7 @@ Button id: base property var outputDevice: null property var matched: updateOnSync() - text: matched == true ? "Yes" : "No" + text: matched == true ? catalog.i18nc("@label:extruder label", "Yes") : catalog.i18nc("@label:extruder label", "No") width: parent.width height: parent.height diff --git a/resources/qml/Menus/MaterialMenu.qml b/resources/qml/Menus/MaterialMenu.qml index d81e0c86c3..64b3130724 100644 --- a/resources/qml/Menus/MaterialMenu.qml +++ b/resources/qml/Menus/MaterialMenu.qml @@ -63,8 +63,7 @@ Menu exclusiveGroup: group onTriggered: { - var activeExtruderIndex = Cura.ExtruderManager.activeExtruderIndex; - Cura.MachineManager.setMaterial(activeExtruderIndex, model.container_node); + Cura.MachineManager.setMaterial(extruderIndex, model.container_node); } } onObjectAdded: brandMaterialsMenu.insertItem(index, object) diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 0369f492b4..77537b0af2 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -100,8 +100,8 @@ Item { if (saveToButton.enabled) { saveToButton.clicked(); } - // prepare button - if (prepareButton.enabled) { + // slice button + if (sliceButton.enabled) { sliceOrStopSlicing(); } } @@ -131,7 +131,7 @@ Item { Row { id: additionalComponentsRow anchors.top: parent.top - anchors.right: saveToButton.visible ? saveToButton.left : (prepareButton.visible ? prepareButton.left : parent.right) + anchors.right: saveToButton.visible ? saveToButton.left : (sliceButton.visible ? sliceButton.left : parent.right) anchors.rightMargin: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").width @@ -159,14 +159,14 @@ Item { onPreferenceChanged: { var autoSlice = UM.Preferences.getValue("general/auto_slice"); - prepareButton.autoSlice = autoSlice; + sliceButton.autoSlice = autoSlice; saveToButton.autoSlice = autoSlice; } } - // Prepare button, only shows if auto_slice is off + // Slice button, only shows if auto_slice is off Button { - id: prepareButton + id: sliceButton tooltip: [1, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@info:tooltip","Slice current printjob") : catalog.i18nc("@info:tooltip","Cancel slicing process") // 1 = not started, 2 = Processing @@ -180,7 +180,7 @@ Item { anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width // 1 = not started, 4 = error, 5 = disabled - text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Prepare") : catalog.i18nc("@label:Printjob", "Cancel") + text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Slice") : catalog.i18nc("@label:Printjob", "Cancel") onClicked: { sliceOrStopSlicing(); diff --git a/resources/setting_visibility/expert.cfg b/resources/setting_visibility/expert.cfg index 6d6b84883c..be0950ec6f 100644 --- a/resources/setting_visibility/expert.cfg +++ b/resources/setting_visibility/expert.cfg @@ -87,6 +87,7 @@ gradual_infill_steps gradual_infill_step_height infill_before_walls min_infill_area +infill_support_enabled skin_preshrink top_skin_preshrink bottom_skin_preshrink @@ -369,7 +370,6 @@ spaghetti_infill_extra_volume support_conical_enabled support_conical_angle support_conical_min_width -infill_hollow magic_fuzzy_skin_enabled magic_fuzzy_skin_thickness magic_fuzzy_skin_point_density