Просмотр исходного кода

Merge branch 'main' into Issue-4835-allow-to-set-print-sequence-manually

Saumya Jain 1 год назад
Родитель
Сommit
355f24e29f

+ 7 - 1
.github/ISSUE_TEMPLATE/SlicingCrash.yaml

@@ -5,6 +5,13 @@ body:
 - type: markdown
   attributes:
     value: |
+       ### 💥 Slicing Crash Analysis Tool 💥
+       We are taking steps to analyze an increase in reported crashes more systematically. We'll need some help with that. 😇
+       Before filling out the report below, we want you to try a special Cura 5.7 Alpha.
+       This version of Cura has an updated slicing engine that will automatically send a report to the Cura Team for analysis. 
+       #### [You can find the downloads here](https://github.com/Ultimaker/Cura/discussions/18080) #### 
+       If you still encounter a crash you are still welcome to report the issue so we can use your model as a test case, you can find instructions on how to do that below.      
+       
        ### Project File
        **⚠️ Before you continue, we need your project file to troubleshoot a slicing crash.**
        It contains the printer and settings we need for troubleshooting. 
@@ -68,4 +75,3 @@ body:
     description: You can add the zip file and additional information that is relevant to the issue in the comments below.
   validations:
     required: true
-

+ 1 - 1
.github/workflows/installers.yml

@@ -58,7 +58,7 @@ jobs:
       enterprise: ${{ github.event.inputs.enterprise == 'true' }}
       staging: ${{ github.event.inputs.staging == 'true' }}
       architecture: X64
-      operating_system: windows-2022
+      operating_system: self-hosted-Windows-X64
     secrets: inherit
 
   linux-installer:

+ 2 - 1
.github/workflows/windows.yml

@@ -34,9 +34,10 @@ on:
       operating_system:
         description: 'OS'
         required: true
-        default: 'windows-2022'
+        default: 'self-hosted-Windows-X64'
         type: choice
         options:
+          - self-hosted-Windows-X64
           - windows-2022
 
 jobs:

+ 10 - 189
UltiMaker-Cura.spec.jinja

@@ -55,7 +55,8 @@ exe = EXE(
     target_arch={{ target_arch }},
     codesign_identity=os.getenv('CODESIGN_IDENTITY', None),
     entitlements_file={{ entitlements_file }},
-    icon={{ icon }}
+    icon={{ icon }},
+    contents_directory='.'
 )
 
 coll = COLLECT(
@@ -70,188 +71,7 @@ coll = COLLECT(
 )
 
 {% if macos == true %}
-# PyInstaller seems to copy everything in the resource folder for the MacOS, this causes issues with codesigning and notarizing
-# The folder structure should adhere to the one specified in Table 2-5
-# https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW1
-# The class below is basically ducktyping the BUNDLE class of PyInstaller and using our own `assemble` method for more fine-grain and specific
-# control. Some code of the method below is copied from:
-# https://github.com/pyinstaller/pyinstaller/blob/22d1d2a5378228744cc95f14904dae1664df32c4/PyInstaller/building/osx.py#L115
-#-----------------------------------------------------------------------------
-# Copyright (c) 2005-2022, PyInstaller Development Team.
-#
-# Distributed under the terms of the GNU General Public License (version 2
-# or later) with exception for distributing the bootloader.
-#
-# The full license is in the file COPYING.txt, distributed with this software.
-#
-# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
-#-----------------------------------------------------------------------------
-
-import plistlib
-import shutil
-import PyInstaller.utils.osx as osxutils
-from pathlib import Path
-from PyInstaller.building.osx import BUNDLE
-from PyInstaller.building.utils import (_check_path_overlap, _rmtree, add_suffix_to_extension, checkCache)
-from PyInstaller.building.datastruct import logger
-from PyInstaller.building.icon import normalize_icon_type
-
-
-class UMBUNDLE(BUNDLE):
-    def assemble(self):
-        from PyInstaller.config import CONF
-
-        if _check_path_overlap(self.name) and os.path.isdir(self.name):
-            _rmtree(self.name)
-        logger.info("Building BUNDLE %s", self.tocbasename)
-
-        # Create a minimal Mac bundle structure.
-        macos_path = Path(self.name, "Contents", "MacOS")
-        resources_path = Path(self.name, "Contents", "Resources")
-        frameworks_path = Path(self.name, "Contents", "Frameworks")
-        os.makedirs(macos_path)
-        os.makedirs(resources_path)
-        os.makedirs(frameworks_path)
-
-        # Makes sure the icon exists and attempts to convert to the proper format if applicable
-        self.icon = normalize_icon_type(self.icon, ("icns",), "icns", CONF["workpath"])
-
-        # Ensure icon path is absolute
-        self.icon = os.path.abspath(self.icon)
-
-        # Copy icns icon to Resources directory.
-        shutil.copy(self.icon, os.path.join(self.name, 'Contents', 'Resources'))
-
-        # Key/values for a minimal Info.plist file
-        info_plist_dict = {
-            "CFBundleDisplayName": self.appname,
-            "CFBundleName": self.appname,
-
-            # Required by 'codesign' utility.
-            # The value for CFBundleIdentifier is used as the default unique name of your program for Code Signing
-            # purposes. It even identifies the APP for access to restricted OS X areas like Keychain.
-            #
-            # The identifier used for signing must be globally unique. The usual form for this identifier is a
-            # hierarchical name in reverse DNS notation, starting with the toplevel domain, followed by the company
-            # name, followed by the department within the company, and ending with the product name. Usually in the
-            # form: com.mycompany.department.appname
-            # CLI option --osx-bundle-identifier sets this value.
-            "CFBundleIdentifier": self.bundle_identifier,
-            "CFBundleExecutable": os.path.basename(self.exename),
-            "CFBundleIconFile": os.path.basename(self.icon),
-            "CFBundleInfoDictionaryVersion": "6.0",
-            "CFBundlePackageType": "APPL",
-            "CFBundleVersionString": self.version,
-            "CFBundleShortVersionString": self.version,
-        }
-
-        # Set some default values. But they still can be overwritten by the user.
-        if self.console:
-            # Setting EXE console=True implies LSBackgroundOnly=True.
-            info_plist_dict['LSBackgroundOnly'] = True
-        else:
-            # Let's use high resolution by default.
-            info_plist_dict['NSHighResolutionCapable'] = True
-
-        # Merge info_plist settings from spec file
-        if isinstance(self.info_plist, dict) and self.info_plist:
-            info_plist_dict.update(self.info_plist)
-
-        plist_filename = os.path.join(self.name, "Contents", "Info.plist")
-        with open(plist_filename, "wb") as plist_fh:
-            plistlib.dump(info_plist_dict, plist_fh)
-
-        links = []
-        _QT_BASE_PATH = {'PySide2', 'PySide6', 'PyQt5', 'PyQt6', 'PySide6'}
-        for inm, fnm, typ in self.toc:
-            # Adjust name for extensions, if applicable
-            inm, fnm, typ = add_suffix_to_extension(inm, fnm, typ)
-            inm = Path(inm)
-            fnm = Path(fnm)
-            # Copy files from cache. This ensures that are used files with relative paths to dynamic library
-            # dependencies (@executable_path)
-            if typ in ('EXTENSION', 'BINARY') or (typ == 'DATA' and inm.suffix == '.so'):
-                if any(['.' in p for p in inm.parent.parts]):
-                    inm = Path(inm.name)
-                fnm = Path(checkCache(
-                    str(fnm),
-                    strip = self.strip,
-                    upx = self.upx,
-                    upx_exclude = self.upx_exclude,
-                    dist_nm = str(inm),
-                    target_arch = self.target_arch,
-                    codesign_identity = self.codesign_identity,
-                    entitlements_file = self.entitlements_file,
-                    strict_arch_validation = (typ == 'EXTENSION'),
-                ))
-                frame_dst = frameworks_path.joinpath(inm)
-                if not frame_dst.exists():
-                    if frame_dst.is_dir():
-                        os.makedirs(frame_dst, exist_ok = True)
-                    else:
-                        os.makedirs(frame_dst.parent, exist_ok = True)
-                shutil.copy(fnm, frame_dst, follow_symlinks = True)
-                macos_dst = macos_path.joinpath(inm)
-                if not macos_dst.exists():
-                    if macos_dst.is_dir():
-                        os.makedirs(macos_dst, exist_ok = True)
-                    else:
-                        os.makedirs(macos_dst.parent, exist_ok = True)
-
-                    # Create relative symlink to the framework
-                    symlink_to = Path(*[".." for p in macos_dst.relative_to(macos_path).parts], "Frameworks").joinpath(
-                        frame_dst.relative_to(frameworks_path))
-                    try:
-                        macos_dst.symlink_to(symlink_to)
-                    except FileExistsError:
-                        pass
-            else:
-                if typ == 'DATA':
-                    if any(['.' in p for p in inm.parent.parts]) or inm.suffix == '.so':
-                        # Skip info dist egg and some not needed folders in tcl and tk, since they all contain dots in their files
-                        logger.warning(f"Skipping DATA file {inm}")
-                        continue
-                    res_dst = resources_path.joinpath(inm)
-                    if not res_dst.exists():
-                        if res_dst.is_dir():
-                            os.makedirs(res_dst, exist_ok = True)
-                        else:
-                            os.makedirs(res_dst.parent, exist_ok = True)
-                    shutil.copy(fnm, res_dst, follow_symlinks = True)
-                    macos_dst = macos_path.joinpath(inm)
-                    if not macos_dst.exists():
-                        if macos_dst.is_dir():
-                            os.makedirs(macos_dst, exist_ok = True)
-                        else:
-                            os.makedirs(macos_dst.parent, exist_ok = True)
-
-                        # Create relative symlink to the resource
-                        symlink_to = Path(*[".." for p in macos_dst.relative_to(macos_path).parts], "Resources").joinpath(
-                            res_dst.relative_to(resources_path))
-                        try:
-                            macos_dst.symlink_to(symlink_to)
-                        except FileExistsError:
-                            pass
-                else:
-                    macos_dst = macos_path.joinpath(inm)
-                    if not macos_dst.exists():
-                        if macos_dst.is_dir():
-                            os.makedirs(macos_dst, exist_ok = True)
-                        else:
-                            os.makedirs(macos_dst.parent, exist_ok = True)
-                        shutil.copy(fnm, macos_dst, follow_symlinks = True)
-
-        # Sign the bundle
-        logger.info('Signing the BUNDLE...')
-        try:
-            osxutils.sign_binary(self.name, self.codesign_identity, self.entitlements_file, deep = True)
-        except Exception as e:
-            logger.warning(f"Error while signing the bundle: {e}")
-            logger.warning("You will need to sign the bundle manually!")
-
-        logger.info(f"Building BUNDLE {self.tocbasename} completed successfully.")
-
-app = UMBUNDLE(
+app = BUNDLE(
     coll,
     name='{{ display_name }}.app',
     icon={{ icon }},
@@ -271,9 +91,10 @@ app = UMBUNDLE(
                 'CFBundleURLSchemes': ['cura', 'slicer'],
         }],
         'CFBundleDocumentTypes': [{
-                'CFBundleTypeRole': 'Viewer',
-                'CFBundleTypeExtensions': ['*'],
-                'CFBundleTypeName': 'Model Files',
-            }]
-        },
-){% endif %}
+            'CFBundleTypeRole': 'Viewer',
+            'CFBundleTypeExtensions': ['stl', 'obj', '3mf', 'gcode', 'ufp'],
+            'CFBundleTypeName': 'Model Files',
+        }]
+    },
+)
+{% endif %}

+ 2 - 4
conandata.yml

@@ -1,12 +1,11 @@
-version: "5.7.0-alpha.0"
+version: "5.7.0-alpha.1"
 requirements:
   - "uranium/(latest)@ultimaker/testing"
   - "curaengine/(latest)@ultimaker/testing"
   - "cura_binary_data/(latest)@ultimaker/testing"
   - "fdm_materials/(latest)@ultimaker/testing"
-  - "curaengine_plugin_gradual_flow/(latest)@ultimaker/stable"
+  - "curaengine_plugin_gradual_flow/0.1.0-beta.2"
   - "dulcificum/latest@ultimaker/testing"
-  - "pyarcus/5.3.0"
   - "pysavitar/5.3.0"
   - "pynest2d/5.3.0"
   - "curaengine_grpc_definitions/(latest)@ultimaker/testing"
@@ -119,7 +118,6 @@ pyinstaller:
         - "sqlite3"
         - "trimesh"
         - "win32ctypes"
-        - "PyQt6"
         - "PyQt6.QtNetwork"
         - "PyQt6.sip"
         - "stl"

+ 24 - 6
conanfile.py

@@ -1,4 +1,5 @@
 import os
+from io import StringIO
 from pathlib import Path
 
 from jinja2 import Template
@@ -150,6 +151,7 @@ class CuraConan(ConanFile):
         return "None"
 
     def _conan_installs(self):
+        self.output.info("Collecting conan installs")
         conan_installs = {}
 
         # list of conan installs
@@ -161,13 +163,22 @@ class CuraConan(ConanFile):
         return conan_installs
 
     def _python_installs(self):
+        self.output.info("Collecting python installs")
         python_installs = {}
 
         # list of python installs
-        python_ins_cmd = f"python -c \"import pkg_resources; print(';'.join([(s.key+','+ s.version) for s in pkg_resources.working_set]))\""
-        from six import StringIO
+        run_env = VirtualRunEnv(self)
+        env = run_env.environment()
+        env.prepend_path("PYTHONPATH", str(self._site_packages.as_posix()))
+        venv_vars = env.vars(self, scope = "run")
+
+        outer = '"' if self.settings.os == "Windows" else "'"
+        inner = "'" if self.settings.os == "Windows" else '"'
         buffer = StringIO()
-        self.run(python_ins_cmd, run_environment= True, env = "conanrun",  output=buffer)
+        with venv_vars.apply():
+            self.run(f"""python -c {outer}import pkg_resources;  print({inner};{inner}.join([(s.key+{inner},{inner}+ s.version) for s in pkg_resources.working_set])){outer}""",
+                          env = "conanrun",
+                          output = buffer)
 
         packages = str(buffer.getvalue()).split("-----------------\n")
         packages = packages[1].strip('\r\n').split(";")
@@ -242,7 +253,7 @@ class CuraConan(ConanFile):
                 self.output.warning(f"Source path for binary {binary['binary']} does not exist")
                 continue
 
-            for bin in Path(src_path).glob(binary["binary"] + "*[.exe|.dll|.so|.dylib|.so.|.pdb]*"):
+            for bin in Path(src_path).glob(binary["binary"] + "*[.exe|.dll|.so|.dylib|.so.]*"):
                 binaries.append((str(bin), binary["dst"]))
             for bin in Path(src_path).glob(binary["binary"]):
                 binaries.append((str(bin), binary["dst"]))
@@ -320,6 +331,8 @@ class CuraConan(ConanFile):
             self.options["openssl"].shared = True
         if self.conf.get("user.curaengine:sentry_url", "", check_type=str) != "":
             self.options["curaengine"].enable_sentry = True
+            self.options["arcus"].enable_sentry = True
+            self.options["clipper"].enable_sentry = True
 
     def validate(self):
         version = self.conf.get("user.cura:version", default = self.version, check_type = str)
@@ -335,6 +348,7 @@ class CuraConan(ConanFile):
             for req in self.conan_data["requirements_internal"]:
                 self.requires(req)
         self.requires("cpython/3.10.4@ultimaker/stable")
+        self.requires("clipper/6.4.2@ultimaker/stable")
         self.requires("openssl/3.2.0")
         self.requires("boost/1.82.0")
         self.requires("spdlog/1.12.0")
@@ -417,7 +431,6 @@ class CuraConan(ConanFile):
             )
 
         if self.options.get_safe("enable_i18n", False) and self._i18n_options["extract"]:
-            # Update the po and pot files
             vb = VirtualBuildEnv(self)
             vb.generate()
 
@@ -502,10 +515,14 @@ echo "CURA_APP_NAME={{ cura_app_name }}" >> ${{ env_prefix }}GITHUB_ENV
 
         if self.in_local_cache:
             self.runenv_info.append_path("PYTHONPATH", os.path.join(self.package_folder, "site-packages"))
+            self.env_info.PYTHONPATH.append(os.path.join(self.package_folder, "site-packages"))
             self.runenv_info.append_path("PYTHONPATH", os.path.join(self.package_folder, "plugins"))
+            self.env_info.PYTHONPATH.append(os.path.join(self.package_folder, "plugins"))
         else:
             self.runenv_info.append_path("PYTHONPATH", self.source_folder)
+            self.env_info.PYTHONPATH.append(self.source_folder)
             self.runenv_info.append_path("PYTHONPATH", os.path.join(self.source_folder, "plugins"))
+            self.env_info.PYTHONPATH.append(os.path.join(self.source_folder, "plugins"))
 
     def package_id(self):
         self.info.clear()
@@ -518,7 +535,8 @@ echo "CURA_APP_NAME={{ cura_app_name }}" >> ${{ env_prefix }}GITHUB_ENV
         del self.info.options.cloud_api_version
         del self.info.options.display_name
         del self.info.options.cura_debug_mode
-        self.options.rm_safe("enable_i18n")
+        if self.options.get_safe("enable_i18n", False):
+            del self.info.options.enable_i18n
 
         # TODO: Use the hash of requirements.txt and requirements-ultimaker.txt, Because changing these will actually result in a different
         #  Cura. This is needed because the requirements.txt aren't managed by Conan and therefor not resolved in the package_id. This isn't

+ 25 - 1
cura/CuraActions.py

@@ -3,10 +3,11 @@
 
 from typing import List, cast
 
-from PyQt6.QtCore import QObject, QUrl, QMimeData
+from PyQt6.QtCore import QObject, QUrl, pyqtSignal, pyqtProperty
 from PyQt6.QtGui import QDesktopServices
 from PyQt6.QtWidgets import QApplication
 
+from UM.Application import Application
 from UM.Event import CallFunctionEvent
 from UM.FlameProfiler import pyqtSlot
 from UM.Math.Vector import Vector
@@ -37,6 +38,10 @@ class CuraActions(QObject):
     def __init__(self, parent: QObject = None) -> None:
         super().__init__(parent)
 
+        self._operation_stack = Application.getInstance().getOperationStack()
+        self._operation_stack.changed.connect(self._onUndoStackChanged)
+
+    undoStackChanged = pyqtSignal()
     @pyqtSlot()
     def openDocumentation(self) -> None:
         # Starting a web browser from a signal handler connected to a menu will crash on windows.
@@ -45,6 +50,25 @@ class CuraActions(QObject):
         event = CallFunctionEvent(self._openUrl, [QUrl("https://ultimaker.com/en/resources/manuals/software?utm_source=cura&utm_medium=software&utm_campaign=dropdown-documentation")], {})
         cura.CuraApplication.CuraApplication.getInstance().functionEvent(event)
 
+    @pyqtProperty(bool, notify=undoStackChanged)
+    def canUndo(self):
+        return self._operation_stack.canUndo()
+
+    @pyqtProperty(bool, notify=undoStackChanged)
+    def canRedo(self):
+        return self._operation_stack.canRedo()
+
+    @pyqtSlot()
+    def undo(self):
+        self._operation_stack.undo()
+
+    @pyqtSlot()
+    def redo(self):
+        self._operation_stack.redo()
+
+    def _onUndoStackChanged(self):
+        self.undoStackChanged.emit()
+
     @pyqtSlot()
     def openBugReportPage(self) -> None:
         event = CallFunctionEvent(self._openUrl, [QUrl("https://github.com/Ultimaker/Cura/issues/new/choose")], {})

+ 65 - 23
cura/CuraApplication.py

@@ -15,13 +15,13 @@ import numpy
 from PyQt6.QtCore import QObject, QTimer, QUrl, QUrlQuery, pyqtSignal, pyqtProperty, QEvent, pyqtEnum, QCoreApplication, \
     QByteArray
 from PyQt6.QtGui import QColor, QIcon
-from PyQt6.QtQml import qmlRegisterUncreatableType, qmlRegisterUncreatableMetaObject, qmlRegisterSingletonType, qmlRegisterType
+from PyQt6.QtQml import qmlRegisterUncreatableMetaObject, qmlRegisterSingletonType, qmlRegisterType
 from PyQt6.QtWidgets import QMessageBox
 
 import UM.Util
 import cura.Settings.cura_empty_instance_containers
 from UM.Application import Application
-from UM.Decorators import override
+from UM.Decorators import override, deprecated
 from UM.FlameProfiler import pyqtSlot
 from UM.Logger import Logger
 from UM.Math.AxisAlignedBox import AxisAlignedBox
@@ -104,7 +104,8 @@ from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
 from cura.Settings.SidebarCustomMenuItemsModel import SidebarCustomMenuItemsModel
 from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
 from cura.TaskManagement.OnExitCallbackManager import OnExitCallbackManager
-from cura.UI import CuraSplashScreen, MachineActionManager, PrintInformation
+from cura.UI import CuraSplashScreen, PrintInformation
+from cura.UI.MachineActionManager import MachineActionManager
 from cura.UI.AddPrinterPagesModel import AddPrinterPagesModel
 from cura.UI.MachineSettingsManager import MachineSettingsManager
 from cura.UI.ObjectsModel import ObjectsModel
@@ -180,6 +181,7 @@ class CuraApplication(QtApplication):
 
         # Variables set from CLI
         self._files_to_open = []
+        self._urls_to_open = []
         self._use_single_instance = False
 
         self._single_instance = None
@@ -187,12 +189,12 @@ class CuraApplication(QtApplication):
 
         self._cura_formula_functions = None  # type: Optional[CuraFormulaFunctions]
 
-        self._machine_action_manager = None  # type: Optional[MachineActionManager.MachineActionManager]
+        self._machine_action_manager: Optional[MachineActionManager] = None
 
         self.empty_container = None  # type: EmptyInstanceContainer
         self.empty_definition_changes_container = None  # type: EmptyInstanceContainer
         self.empty_variant_container = None  # type: EmptyInstanceContainer
-        self.empty_intent_container = None  # type: EmptyInstanceContainer 
+        self.empty_intent_container = None  # type: EmptyInstanceContainer
         self.empty_material_container = None  # type: EmptyInstanceContainer
         self.empty_quality_container = None  # type: EmptyInstanceContainer
         self.empty_quality_changes_container = None  # type: EmptyInstanceContainer
@@ -335,7 +337,7 @@ class CuraApplication(QtApplication):
         for filename in self._cli_args.file:
             url = QUrl(filename)
             if url.scheme() in self._supported_url_schemes:
-                self._open_url_queue.append(url)
+                self._urls_to_open.append(url)
             else:
                 self._files_to_open.append(os.path.abspath(filename))
 
@@ -354,11 +356,11 @@ class CuraApplication(QtApplication):
         self.__addAllEmptyContainers()
         self.__setLatestResouceVersionsForVersionUpgrade()
 
-        self._machine_action_manager = MachineActionManager.MachineActionManager(self)
+        self._machine_action_manager = MachineActionManager(self)
         self._machine_action_manager.initialize()
 
     def __sendCommandToSingleInstance(self):
-        self._single_instance = SingleInstance(self, self._files_to_open)
+        self._single_instance = SingleInstance(self, self._files_to_open, self._urls_to_open)
 
         # If we use single instance, try to connect to the single instance server, send commands, and then exit.
         # If we cannot find an existing single instance server, this is the only instance, so just keep going.
@@ -375,9 +377,15 @@ class CuraApplication(QtApplication):
             Resources.addExpectedDirNameInData(dir_name)
 
         app_root = os.path.abspath(os.path.join(os.path.dirname(sys.executable)))
-        Resources.addSecureSearchPath(os.path.join(app_root, "share", "cura", "resources"))
 
-        Resources.addSecureSearchPath(os.path.join(self._app_install_dir, "share", "cura", "resources"))
+        if platform.system() == "Darwin":
+            Resources.addSecureSearchPath(os.path.join(app_root, "Resources", "share", "cura", "resources"))
+            Resources.addSecureSearchPath(
+                os.path.join(self._app_install_dir, "Resources", "share", "cura", "resources"))
+        else:
+            Resources.addSecureSearchPath(os.path.join(app_root, "share", "cura", "resources"))
+            Resources.addSecureSearchPath(os.path.join(self._app_install_dir, "share", "cura", "resources"))
+
         if not hasattr(sys, "frozen"):
             cura_data_root = os.environ.get('CURA_DATA_ROOT', None)
             if cura_data_root:
@@ -959,6 +967,8 @@ class CuraApplication(QtApplication):
             self.callLater(self._openFile, file_name)
         for file_name in self._open_file_queue:  # Open all the files that were queued up while plug-ins were loading.
             self.callLater(self._openFile, file_name)
+        for url in self._urls_to_open:
+            self.callLater(self._openUrl, url)
         for url in self._open_url_queue:
             self.callLater(self._openUrl, url)
 
@@ -1098,6 +1108,10 @@ class CuraApplication(QtApplication):
             self._object_manager = ObjectsModel(self)
         return self._object_manager
 
+    @pyqtSlot(str, result = "QVariantList")
+    def getSupportedActionMachineList(self, definition_id: str) -> List["MachineAction"]:
+        return self._machine_action_manager.getSupportedActions(self._machine_manager.getDefinitionByMachineId(definition_id))
+
     @pyqtSlot(result = QObject)
     def getExtrudersModel(self, *args) -> "ExtrudersModel":
         if self._extruders_model is None:
@@ -1133,14 +1147,16 @@ class CuraApplication(QtApplication):
             self._setting_inheritance_manager = SettingInheritanceManager.createSettingInheritanceManager()
         return self._setting_inheritance_manager
 
-    def getMachineActionManager(self, *args: Any) -> MachineActionManager.MachineActionManager:
+    @pyqtSlot(result = QObject)
+    def getMachineActionManager(self, *args: Any) -> MachineActionManager:
         """Get the machine action manager
 
         We ignore any *args given to this, as we also register the machine manager as qml singleton.
         It wants to give this function an engine and script engine, but we don't care about that.
         """
 
-        return cast(MachineActionManager.MachineActionManager, self._machine_action_manager)
+        return  self._machine_action_manager
+
 
     @pyqtSlot(result = QObject)
     def getMaterialManagementModel(self) -> MaterialManagementModel:
@@ -1154,7 +1170,8 @@ class CuraApplication(QtApplication):
             self._quality_management_model = QualityManagementModel(parent = self)
         return self._quality_management_model
 
-    def getSimpleModeSettingsManager(self, *args):
+    @pyqtSlot(result=QObject)
+    def getSimpleModeSettingsManager(self)-> SimpleModeSettingsManager:
         if self._simple_mode_settings_manager is None:
             self._simple_mode_settings_manager = SimpleModeSettingsManager()
         return self._simple_mode_settings_manager
@@ -1197,16 +1214,43 @@ class CuraApplication(QtApplication):
 
         return self._print_information
 
-    def getQualityProfilesDropDownMenuModel(self, *args, **kwargs):
+    @pyqtSlot(result=QObject)
+    def getQualityProfilesDropDownMenuModel(self, *args, **kwargs)-> QualityProfilesDropDownMenuModel:
         if self._quality_profile_drop_down_menu_model is None:
             self._quality_profile_drop_down_menu_model = QualityProfilesDropDownMenuModel(self)
         return self._quality_profile_drop_down_menu_model
 
-    def getCustomQualityProfilesDropDownMenuModel(self, *args, **kwargs):
+    @pyqtSlot(result=QObject)
+    def getCustomQualityProfilesDropDownMenuModel(self, *args, **kwargs)->CustomQualityProfilesDropDownMenuModel:
         if self._custom_quality_profile_drop_down_menu_model is None:
             self._custom_quality_profile_drop_down_menu_model = CustomQualityProfilesDropDownMenuModel(self)
         return self._custom_quality_profile_drop_down_menu_model
 
+    @deprecated("SimpleModeSettingsManager is deprecated and will be removed in major SDK release, Use getSimpleModeSettingsManager() instead", since = "5.7.0")
+    def getSimpleModeSettingsManagerWrapper(self, *args, **kwargs):
+        return self.getSimpleModeSettingsManager()
+
+    @deprecated("MachineActionManager is deprecated and will be removed in major SDK release, Use getMachineActionManager() instead", since="5.7.0")
+    def getMachineActionManagerWrapper(self, *args, **kwargs):
+        return self.getMachineActionManager()
+
+    @deprecated("QualityManagementModel is deprecated and will be removed in major SDK release, Use getQualityManagementModel() instead", since="5.7.0")
+    def getQualityManagementModelWrapper(self, *args, **kwargs):
+        return self.getQualityManagementModel()
+
+    @deprecated("MaterialManagementModel is deprecated and will be removed in major SDK release, Use getMaterialManagementModel() instead", since = "5.7.0")
+    def getMaterialManagementModelWrapper(self, *args, **kwargs):
+        return self.getMaterialManagementModel()
+
+    @deprecated("QualityProfilesDropDownMenuModel is deprecated and will be removed in major SDK release, Use getQualityProfilesDropDownMenuModel() instead", since = "5.7.0")
+    def getQualityProfilesDropDownMenuModelWrapper(self, *args, **kwargs):
+        return self.getQualityProfilesDropDownMenuModel()
+
+    @deprecated("CustomQualityProfilesDropDownMenuModel is deprecated and will be removed in major SDK release, Use getCustomQualityProfilesDropDownMenuModel() instead", since = "5.7.0")
+    def getCustomQualityProfilesDropDownMenuModelWrapper(self,  *args, **kwargs):
+        return self.getCustomQualityProfilesDropDownMenuModel()
+
+
     def getCuraAPI(self, *args, **kwargs) -> "CuraAPI":
         return self._cura_API
 
@@ -1236,8 +1280,8 @@ class CuraApplication(QtApplication):
         qmlRegisterSingletonType(MachineManager, "Cura", 1, 0, self.getMachineManager, "MachineManager")
         qmlRegisterSingletonType(IntentManager, "Cura", 1, 6, self.getIntentManager, "IntentManager")
         qmlRegisterSingletonType(SettingInheritanceManager, "Cura", 1, 0, self.getSettingInheritanceManager, "SettingInheritanceManager")
-        qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, self.getSimpleModeSettingsManager, "SimpleModeSettingsManager")
-        qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, self.getMachineActionManager, "MachineActionManager")
+        qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, self.getSimpleModeSettingsManagerWrapper, "SimpleModeSettingsManager")
+        qmlRegisterSingletonType(MachineActionManager, "Cura", 1, 0, self.getMachineActionManagerWrapper, "MachineActionManager")
 
         self.processEvents()
         qmlRegisterType(NetworkingUtil, "Cura", 1, 5, "NetworkingUtil")
@@ -1262,16 +1306,14 @@ class CuraApplication(QtApplication):
         qmlRegisterType(FavoriteMaterialsModel, "Cura", 1, 0, "FavoriteMaterialsModel")
         qmlRegisterType(GenericMaterialsModel, "Cura", 1, 0, "GenericMaterialsModel")
         qmlRegisterType(MaterialBrandsModel, "Cura", 1, 0, "MaterialBrandsModel")
-        qmlRegisterSingletonType(QualityManagementModel, "Cura", 1, 0, self.getQualityManagementModel, "QualityManagementModel")
-        qmlRegisterSingletonType(MaterialManagementModel, "Cura", 1, 5, self.getMaterialManagementModel, "MaterialManagementModel")
+        qmlRegisterSingletonType(QualityManagementModel, "Cura", 1, 0, self.getQualityManagementModelWrapper,"QualityManagementModel")
+        qmlRegisterSingletonType(MaterialManagementModel, "Cura", 1, 5, self.getMaterialManagementModelWrapper,"MaterialManagementModel")
 
         self.processEvents()
         qmlRegisterType(DiscoveredPrintersModel, "Cura", 1, 0, "DiscoveredPrintersModel")
         qmlRegisterType(DiscoveredCloudPrintersModel, "Cura", 1, 7, "DiscoveredCloudPrintersModel")
-        qmlRegisterSingletonType(QualityProfilesDropDownMenuModel, "Cura", 1, 0,
-                                 self.getQualityProfilesDropDownMenuModel, "QualityProfilesDropDownMenuModel")
-        qmlRegisterSingletonType(CustomQualityProfilesDropDownMenuModel, "Cura", 1, 0,
-                                 self.getCustomQualityProfilesDropDownMenuModel, "CustomQualityProfilesDropDownMenuModel")
+        qmlRegisterSingletonType(QualityProfilesDropDownMenuModel, "Cura", 1, 0, self.getQualityProfilesDropDownMenuModelWrapper, "QualityProfilesDropDownMenuModel")
+        qmlRegisterSingletonType(CustomQualityProfilesDropDownMenuModel, "Cura", 1, 0, self.getCustomQualityProfilesDropDownMenuModelWrapper, "CustomQualityProfilesDropDownMenuModel")
         qmlRegisterType(NozzleModel, "Cura", 1, 0, "NozzleModel")
         qmlRegisterType(IntentModel, "Cura", 1, 6, "IntentModel")
         qmlRegisterType(IntentCategoryModel, "Cura", 1, 6, "IntentCategoryModel")

+ 1 - 1
cura/Machines/Models/ExtrudersModel.py

@@ -227,7 +227,7 @@ class ExtrudersModel(ListModel):
                     "material_brand": "",
                     "color_name": "",
                     "material_type": "",
-                    "material_label": ""
+                    "material_name": ""
                 }
                 items.append(item)
             if self._items != items:

+ 2 - 0
cura/OAuth2/AuthorizationHelpers.py

@@ -40,6 +40,7 @@ class AuthorizationHelpers:
         """
         data = {
             "client_id": self._settings.CLIENT_ID if self._settings.CLIENT_ID is not None else "",
+            "client_secret": self._settings.CLIENT_SECRET if self._settings.CLIENT_SECRET is not None else "",
             "redirect_uri": self._settings.CALLBACK_URL if self._settings.CALLBACK_URL is not None else "",
             "grant_type": "authorization_code",
             "code": authorization_code,
@@ -64,6 +65,7 @@ class AuthorizationHelpers:
         Logger.log("d", "Refreshing the access token for [%s]", self._settings.OAUTH_SERVER_URL)
         data = {
             "client_id": self._settings.CLIENT_ID if self._settings.CLIENT_ID is not None else "",
+            "client_secret": self._settings.CLIENT_SECRET if self._settings.CLIENT_SECRET is not None else "",
             "redirect_uri": self._settings.CALLBACK_URL if self._settings.CALLBACK_URL is not None else "",
             "grant_type": "refresh_token",
             "refresh_token": refresh_token,

Некоторые файлы не были показаны из-за большого количества измененных файлов