Browse Source

Merge branch 'main' into CURA-10004_fix_crash_small_rotations

Saumya Jain 1 year ago
parent
commit
47b4320512

+ 33 - 8
.github/workflows/installers.yml

@@ -31,8 +31,8 @@ on:
         type: boolean
 
   schedule:
-    # Daily at 5:20 CET
-    - cron: '20 4 * * *'
+    # Daily at 5:15 CET
+    - cron: '15 3 * * *'
 
 env:
   CURA_CONAN_VERSION: ${{ inputs.cura_conan_version || 'cura/latest@ultimaker/testing' }}
@@ -41,10 +41,32 @@ env:
   STAGING: ${{ inputs.staging || false }}
 
 jobs:
+  default-values:
+    runs-on: ubuntu-latest
+    outputs:
+      cura_conan_version: ${{ steps.default.outputs.cura_conan_version  }}
+
+    steps:
+    - name: Output default values
+      id: default
+      shell: python
+      run: |
+        import os
+        cura_conan_version = "cura/latest@ultimaker/testing" if "${{ github.event.inputs.cura_conan_version }}" == "" else "${{ github.event.inputs.cura_conan_version }}" 
+        output_env = os.environ["GITHUB_OUTPUT"]
+        content = ""
+        if os.path.exists(output_env):
+            with open(output_env, "r") as f:
+                content = f.read()
+        with open(output_env, "w") as f:
+            f.write(content)
+            f.writelines(f"cura_conan_version={cura_conan_version}\n")
+
   windows-installer:
     uses: ./.github/workflows/windows.yml
+    needs: [ default-values ]
     with:
-      cura_conan_version: ${{ github.event.inputs.CURA_CONAN_VERSION }}
+      cura_conan_version: ${{ needs.default-values.outputs.cura_conan_version }}
       conan_args: ${{ github.event.inputs.conan_args }}
       enterprise: ${{ github.event.inputs.enterprise == 'true' }}
       staging: ${{ github.event.inputs.staging == 'true' }}
@@ -54,8 +76,9 @@ jobs:
 
   linux-installer:
     uses: ./.github/workflows/linux.yml
+    needs: [ default-values ]
     with:
-      cura_conan_version: ${{ github.event.inputs.CURA_CONAN_VERSION }}
+      cura_conan_version: ${{ needs.default-values.outputs.cura_conan_version }}
       conan_args: ${{ github.event.inputs.conan_args }}
       enterprise: ${{ github.event.inputs.enterprise == 'true' }}
       staging: ${{ github.event.inputs.staging == 'true' }}
@@ -65,8 +88,9 @@ jobs:
 
   macos-installer:
     uses: ./.github/workflows/macos.yml
+    needs: [ default-values ]
     with:
-      cura_conan_version: ${{ github.event.inputs.CURA_CONAN_VERSION }}
+      cura_conan_version: ${{ needs.default-values.outputs.cura_conan_version }}
       conan_args: ${{ github.event.inputs.conan_args }}
       enterprise: ${{ github.event.inputs.enterprise == 'true' }}
       staging: ${{ github.event.inputs.staging == 'true' }}
@@ -76,8 +100,9 @@ jobs:
 
   macos-arm-installer:
     uses: ./.github/workflows/macos.yml
+    needs: [ default-values ]
     with:
-      cura_conan_version: ${{ github.event.inputs.CURA_CONAN_VERSION }}
+      cura_conan_version: ${{ needs.default-values.outputs.cura_conan_version }}
       conan_args: ${{ github.event.inputs.conan_args }}
       enterprise: ${{ github.event.inputs.enterprise == 'true' }}
       staging: ${{ github.event.inputs.staging == 'true' }}
@@ -87,7 +112,7 @@ jobs:
 
   # Run and update nightly release when the nightly input is set to true or if the schedule is triggered
   update-nightly-release:
-    if: ${{ always() && (! cancelled()) && contains(needs.*.result, 'success') && (! contains(needs.*.result, 'failure')) && (inputs.nightly || github.event_name == 'schedule') }}
+    if: ${{ inputs.nightly || github.event_name == 'schedule' }}
     runs-on: ubuntu-latest
     needs: [ windows-installer, linux-installer, macos-installer, macos-arm-installer ]
     steps:
@@ -208,7 +233,7 @@ jobs:
           name: ${{ steps.filename.outputs.MAC_ARM_DMG }}-dmg
           path: installers
 
-      - name: Download acOS (ARM-64) pkg installer jobs artifacts
+      - name: Download MacOS (ARM-64) pkg installer jobs artifacts
         uses: actions/download-artifact@v2
         with:
           name: ${{ steps.filename.outputs.MAC_ARM_PKG }}-pkg

+ 6 - 0
cura/BackendPlugin.py

@@ -22,6 +22,10 @@ class BackendPlugin(AdditionalSettingDefinitionsAppender, PluginObject):
         self._process = None
         self._is_running = False
         self._supported_slots: List[int] = []
+        self._use_plugin = True
+
+    def usePlugin(self) -> bool:
+        return self._use_plugin
 
     def getSupportedSlots(self) -> List[int]:
         return self._supported_slots
@@ -55,6 +59,8 @@ class BackendPlugin(AdditionalSettingDefinitionsAppender, PluginObject):
 
         :return: True if the plugin process started successfully, False otherwise.
         """
+        if not self.usePlugin():
+            return False
         try:
             # STDIN needs to be None because we provide no input, but communicate via a local socket instead.
             # The NUL device sometimes doesn't exist on some computers.

+ 17 - 2
cura/Machines/MaterialNode.py

@@ -31,6 +31,7 @@ class MaterialNode(ContainerNode):
         my_metadata = container_registry.findContainersMetadata(id = container_id)[0]
         self.base_file = my_metadata["base_file"]
         self.material_type = my_metadata["material"]
+        self.brand = my_metadata["brand"]
         self.guid = my_metadata["GUID"]
         self._loadAll()
         container_registry.containerRemoved.connect(self._onRemoved)
@@ -80,6 +81,7 @@ class MaterialNode(ContainerNode):
                 # such as "generic_pla_ultimaker_s5_AA_0.4". So we search with the "base_file" which is the material_root_id.
             else:
                 qualities = container_registry.findInstanceContainersMetadata(type = "quality", definition = self.variant.machine.quality_definition, material = self.base_file)
+
             if not qualities:
                 my_material_type = self.material_type
                 if self.variant.machine.has_variants:
@@ -89,9 +91,22 @@ class MaterialNode(ContainerNode):
                 else:
                     qualities_any_material = container_registry.findInstanceContainersMetadata(type = "quality", definition = self.variant.machine.quality_definition)
 
-                all_material_base_files = {material_metadata["base_file"] for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", material = my_material_type)}
+                # First we attempt to find materials that have the same brand but not the right color
+                all_material_base_files_right_brand = {material_metadata["base_file"] for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", material = my_material_type, brand = self.brand)}
+
+                right_brand_no_color_qualities = [quality for quality in qualities_any_material if quality.get("material") in all_material_base_files_right_brand]
 
-                qualities.extend((quality for quality in qualities_any_material if quality.get("material") in all_material_base_files))
+                if right_brand_no_color_qualities:
+                    # We found qualties for materials with the right brand but not with the right color. Use those.
+                    qualities.extend(right_brand_no_color_qualities)
+                else:
+                    # Fall back to generic
+                    all_material_base_files = {material_metadata["base_file"] for material_metadata in
+                                               container_registry.findInstanceContainersMetadata(type="material",
+                                                                                                 material=my_material_type)}
+                    no_brand_no_color_qualities = (quality for quality in qualities_any_material if
+                                                   quality.get("material") in all_material_base_files)
+                    qualities.extend(no_brand_no_color_qualities)
 
                 if not qualities:  # No quality profiles found. Go by GUID then.
                     my_guid = self.guid

+ 4 - 0
cura/Machines/Models/MaterialBrandsModel.py

@@ -44,6 +44,10 @@ class MaterialBrandsModel(BaseMaterialsModel):
             if bool(container_node.getMetaDataEntry("removed", False)):
                 continue
 
+            # Ignore materials that are marked as not visible for whatever reason
+            if not bool(container_node.getMetaDataEntry("visible", True)):
+                continue
+
             # Add brands we haven't seen yet to the dict, skipping generics
             brand = container_node.getMetaDataEntry("brand", "")
             if brand.lower() == "generic":

+ 3 - 0
cura/UltimakerCloud/CloudMaterialSync.py

@@ -148,6 +148,9 @@ class CloudMaterialSync(QObject):
                 continue
             if metadata["id"] == "empty_material":  # Don't export the empty material.
                 continue
+            # Ignore materials that are marked as not visible for whatever reason
+            if not bool(metadata.get("visible", True)):
+                continue
             material = registry.findContainers(id = metadata["id"])[0]
             suffix = registry.getMimeTypeForContainer(type(material)).preferredSuffix
             filename = metadata["id"] + "." + suffix

+ 18 - 4
plugins/3MFReader/ThreeMFWorkspaceReader.py

@@ -1095,7 +1095,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
                 if global_stack.getProperty(key, "settable_per_extruder"):
                     values_to_set_for_extruders[key] = value
                 else:
-                    global_stack.definitionChanges.setProperty(key, "value", value)
+                    if not self._settingIsFromMissingPackage(key, value):
+                        global_stack.definitionChanges.setProperty(key, "value", value)
 
         for position, extruder_stack in extruder_stack_dict.items():
             if position not in self._machine_info.extruder_info_dict:
@@ -1109,7 +1110,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
                 extruder_stack.definitionChanges.setProperty(key, "value", value)
             if parser is not None:
                 for key, value in parser["values"].items():
-                    extruder_stack.definitionChanges.setProperty(key, "value", value)
+                    if not self._settingIsFromMissingPackage(key, value):
+                        extruder_stack.definitionChanges.setProperty(key, "value", value)
 
     def _applyUserChanges(self, global_stack, extruder_stack_dict):
         values_to_set_for_extruder_0 = {}
@@ -1119,7 +1121,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
                 if global_stack.getProperty(key, "settable_per_extruder"):
                     values_to_set_for_extruder_0[key] = value
                 else:
-                    global_stack.userChanges.setProperty(key, "value", value)
+                    if not self._settingIsFromMissingPackage(key, value):
+                        global_stack.userChanges.setProperty(key, "value", value)
 
         for position, extruder_stack in extruder_stack_dict.items():
             if position not in self._machine_info.extruder_info_dict:
@@ -1133,7 +1136,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
                         extruder_stack.userChanges.setProperty(key, "value", value)
                 if parser is not None:
                     for key, value in parser["values"].items():
-                        extruder_stack.userChanges.setProperty(key, "value", value)
+                        if not self._settingIsFromMissingPackage(key, value):
+                            extruder_stack.userChanges.setProperty(key, "value", value)
 
     def _applyVariants(self, global_stack, extruder_stack_dict):
         machine_node = ContainerTree.getInstance().machines[global_stack.definition.getId()]
@@ -1208,6 +1212,15 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
             if key not in _ignored_machine_network_metadata:
                 global_stack.setMetaDataEntry(key, value)
 
+    def _settingIsFromMissingPackage(self, key, value):
+        # Check if the key and value pair is from the missing package
+        for package in self._dialog.missingPackages:
+            if value.startswith("PLUGIN::"):
+                if (package['id'] + "@" + package['package_version']) in value:
+                    Logger.log("w", f"Ignoring {key} value {value} from missing package")
+                    return True
+        return False
+
     def _updateActiveMachine(self, global_stack):
         # Actually change the active machine.
         machine_manager = Application.getInstance().getMachineManager()
@@ -1327,3 +1340,4 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
                 missing_packages.append(package)
 
         return missing_packages
+

+ 2 - 0
plugins/CuraEngineBackend/StartSliceJob.py

@@ -303,6 +303,8 @@ class StartSliceJob(Job):
             self._buildExtruderMessage(extruder_stack)
 
         for plugin in CuraApplication.getInstance().getBackendPlugins():
+            if not plugin.usePlugin():
+                continue
             for slot in plugin.getSupportedSlots():
                 # Right now we just send the message for every slot that we support. A single plugin can support
                 # multiple slots

+ 41 - 2
plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml

@@ -1,7 +1,7 @@
 //Copyright (c) 2022 Ultimaker B.V.
 //Cura is released under the terms of the LGPLv3 or higher.
 
-import QtQuick 2.2
+import QtQuick 2.15
 import QtQuick.Controls 2.15
 
 import UM 1.5 as UM
@@ -234,10 +234,11 @@ Item
                         setDestroyed(true)
                     }
                 }
-
+                property int indexWithFocus: -1
                 delegate: Row
                 {
                     spacing: UM.Theme.getSize("default_margin").width
+                    property var settingLoaderItem: settingLoader.item
                     Loader
                     {
                         id: settingLoader
@@ -340,6 +341,44 @@ Item
                         function onPropertiesChanged() { provider.forcePropertiesChanged() }
                     }
 
+                    Connections
+                    {
+                        target: settingLoader.item
+                        function onFocusReceived()
+                        {
+
+                            contents.indexWithFocus = index
+                            contents.positionViewAtIndex(index, ListView.Contain)
+                        }
+                        function onSetActiveFocusToNextSetting(forward)
+                        {
+                            if (forward == undefined || forward)
+                            {
+                                contents.currentIndex = contents.indexWithFocus + 1
+                                while(contents.currentItem && contents.currentItem.height <= 0)
+                                {
+                                    contents.currentIndex++
+                                }
+                                if (contents.currentItem)
+                                {
+                                    contents.currentItem.settingLoaderItem.focusItem.forceActiveFocus()
+                                }
+                            }
+                            else
+                            {
+                                contents.currentIndex = contents.indexWithFocus - 1
+                                while(contents.currentItem && contents.currentItem.height <= 0)
+                                {
+                                    contents.currentIndex--
+                                }
+                                if (contents.currentItem)
+                                {
+                                    contents.currentItem.settingLoaderItem.focusItem.forceActiveFocus()
+                                }
+                            }
+                        }
+                    }
+
                     Connections
                     {
                         target: UM.ActiveTool

+ 1 - 1
plugins/UM3NetworkPrinting/src/Network/SendMaterialJob.py

@@ -173,7 +173,7 @@ class SendMaterialJob(Job):
 
         result = {}  # type: Dict[str, LocalMaterial]
         all_materials = CuraApplication.getInstance().getContainerRegistry().findInstanceContainersMetadata(type = "material")
-        all_base_files = [material for material in all_materials if material["id"] == material.get("base_file")]  # Don't send materials without base_file: The empty material doesn't need to be sent.
+        all_base_files = [material for material in all_materials if material["id"] == material.get("base_file") and material.get("visible", True)]  # Don't send materials without base_file: The empty material doesn't need to be sent.
 
         # Find the latest version of all material containers in the registry.
         for material_metadata in all_base_files:

+ 72 - 0
plugins/VersionUpgrade/VersionUpgrade54to55/VersionUpgrade54to55.py

@@ -0,0 +1,72 @@
+# Copyright (c) 2023 UltiMaker
+# Cura is released under the terms of the LGPLv3 or higher.
+
+import configparser
+from typing import Tuple, List
+import io
+from UM.VersionUpgrade import VersionUpgrade
+import re
+
+
+
+class VersionUpgrade54to55(VersionUpgrade):
+    profile_regex = re.compile(
+        r"um\_(?P<machine>s(3|5|7))_(?P<core_type>aa|cc|bb)(?P<nozzle_size>0\.(6|4|8))_(?P<material>pla|petg|abs|cpe|cpe_plus|nylon|pc|petcf|tough_pla|tpu)_(?P<layer_height>0\.\d{1,2}mm)")
+
+    @staticmethod
+    def _isUpgradedUltimakerDefinitionId(definition_id: str) -> bool:
+        if definition_id.startswith("ultimaker_s5"):
+            return True
+        if definition_id.startswith("ultimaker_s3"):
+            return True
+        if definition_id.startswith("ultimaker_s7"):
+            return True
+
+        return False
+
+    @staticmethod
+    def _isBrandedMaterialID(material_id: str) -> bool:
+        return material_id.startswith("ultimaker_")
+
+    @staticmethod
+    def upgradeStack(serialized: str, filename: str) -> Tuple[List[str], List[str]]:
+        """
+        Upgrades stacks to have the new version number.
+
+        :param serialized: The original contents of the stack.
+        :param filename: The original file name of the stack.
+        :return: A list of new file names, and a list of the new contents for
+        those files.
+        """
+        parser = configparser.ConfigParser(interpolation = None)
+        parser.read_string(serialized)
+
+        # Update version number.
+        if "general" not in parser:
+            parser["general"] = {}
+
+        extruder_definition_id = parser["containers"]["7"]
+        if parser["metadata"]["type"] == "extruder_train" and VersionUpgrade54to55._isUpgradedUltimakerDefinitionId(extruder_definition_id):
+            # We only need to update certain Ultimaker extruder ID's
+            material_id = parser["containers"]["4"]
+            quality_id = parser["containers"]["3"]
+            intent_id = parser["containers"]["2"]
+            if VersionUpgrade54to55._isBrandedMaterialID(material_id):
+                # We have an Ultimaker branded material ID, so we should change the intent & quality!
+
+                quality_id = VersionUpgrade54to55.profile_regex.sub(
+                    r"um_\g<machine>_\g<core_type>\g<nozzle_size>_um-\g<material>_\g<layer_height>", quality_id)
+
+
+                intent_id = VersionUpgrade54to55.profile_regex.sub(
+                    r"um_\g<machine>_\g<core_type>\g<nozzle_size>_um-\g<material>_\g<layer_height>", intent_id)
+
+            parser["containers"]["3"] = quality_id
+            parser["containers"]["2"] = intent_id
+
+        # We're not changing any settings, but we are changing how certain stacks are handled.
+        parser["general"]["version"] = "6"
+
+        result = io.StringIO()
+        parser.write(result)
+        return [filename], [result.getvalue()]

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