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

Merge branch 'master' of github.com:Ultimaker/Cura

Jack Ha 6 лет назад
Родитель
Сommit
467e814f88

+ 2 - 0
.gitignore

@@ -14,6 +14,7 @@ CuraEngine.exe
 LC_MESSAGES
 .cache
 *.qmlc
+.mypy_cache
 
 #MacOS
 .DS_Store
@@ -38,6 +39,7 @@ plugins/cura-god-mode-plugin
 plugins/cura-siemensnx-plugin
 plugins/CuraBlenderPlugin
 plugins/CuraCloudPlugin
+plugins/CuraDrivePlugin
 plugins/CuraLiveScriptingPlugin
 plugins/CuraOpenSCADPlugin
 plugins/CuraPrintProfileCreator

+ 7 - 1
cmake/CuraTests.cmake

@@ -1,4 +1,4 @@
-# Copyright (c) 2017 Ultimaker B.V.
+# Copyright (c) 2018 Ultimaker B.V.
 # Cura is released under the terms of the LGPLv3 or higher.
 
 enable_testing()
@@ -53,3 +53,9 @@ foreach(_plugin ${_plugins})
         cura_add_test(NAME pytest-${_plugin_name} DIRECTORY ${_plugin_directory} PYTHONPATH "${_plugin_directory}|${CMAKE_SOURCE_DIR}|${URANIUM_DIR}")
     endif()
 endforeach()
+
+#Add code style test.
+add_test(
+    NAME "code-style"
+    COMMAND ${PYTHON_EXECUTABLE} run_mypy.py WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+)

+ 1 - 0
cura/CuraApplication.py

@@ -270,6 +270,7 @@ class CuraApplication(QtApplication):
             "PrepareStage",
             "MonitorStage",
             "LocalFileOutputDevice",
+            "LocalContainerProvider",
 
             # Views:
             "SimpleView",

+ 39 - 40
cura/CuraPackageManager.py

@@ -1,7 +1,7 @@
 # Copyright (c) 2018 Ultimaker B.V.
 # Cura is released under the terms of the LGPLv3 or higher.
 
-from typing import Optional
+from typing import Optional, Dict, Any
 import json
 import os
 import shutil
@@ -15,6 +15,7 @@ from UM.Logger import Logger
 from UM.Resources import Resources
 from UM.Version import Version
 
+
 class CuraPackageManager(QObject):
     Version = 1
 
@@ -25,7 +26,7 @@ class CuraPackageManager(QObject):
     def __init__(self, parent = None):
         super().__init__(parent)
 
-        self._application = parent
+        self._application = Application.getInstance()
         self._container_registry = self._application.getContainerRegistry()
         self._plugin_registry = self._application.getPluginRegistry()
 
@@ -139,6 +140,9 @@ class CuraPackageManager(QObject):
         # Also get all bundled plugins
         all_metadata = self._plugin_registry.getAllMetaData()
         for item in all_metadata:
+            if item == {}:
+                continue
+
             plugin_package_info = self.__convertPluginMetadataToPackageMetadata(item)
             # Only gather the bundled plugins here.
             package_id = plugin_package_info["package_id"]
@@ -187,6 +191,8 @@ class CuraPackageManager(QObject):
         try:
             # Get package information
             package_info = self.getPackageInfo(filename)
+            if not package_info:
+                return
             package_id = package_info["package_id"]
 
             # Check the delayed installation and removal lists first
@@ -279,30 +285,28 @@ class CuraPackageManager(QObject):
             self._purgePackage(package_id)
 
         # Install the package
-        archive = zipfile.ZipFile(filename, "r")
+        with zipfile.ZipFile(filename, "r") as archive:
 
-        temp_dir = tempfile.TemporaryDirectory()
-        archive.extractall(temp_dir.name)
+            temp_dir = tempfile.TemporaryDirectory()
+            archive.extractall(temp_dir.name)
 
-        from cura.CuraApplication import CuraApplication
-        installation_dirs_dict = {
-            "materials": Resources.getStoragePath(CuraApplication.ResourceTypes.MaterialInstanceContainer),
-            "quality": Resources.getStoragePath(CuraApplication.ResourceTypes.QualityInstanceContainer),
-            "plugins": os.path.abspath(Resources.getStoragePath(Resources.Plugins)),
-        }
-
-        for sub_dir_name, installation_root_dir in installation_dirs_dict.items():
-            src_dir_path = os.path.join(temp_dir.name, "files", sub_dir_name)
-            dst_dir_path = os.path.join(installation_root_dir, package_id)
+            from cura.CuraApplication import CuraApplication
+            installation_dirs_dict = {
+                "materials": Resources.getStoragePath(CuraApplication.ResourceTypes.MaterialInstanceContainer),
+                "quality": Resources.getStoragePath(CuraApplication.ResourceTypes.QualityInstanceContainer),
+                "plugins": os.path.abspath(Resources.getStoragePath(Resources.Plugins)),
+            }
 
-            if not os.path.exists(src_dir_path):
-                continue
+            for sub_dir_name, installation_root_dir in installation_dirs_dict.items():
+                src_dir_path = os.path.join(temp_dir.name, "files", sub_dir_name)
+                dst_dir_path = os.path.join(installation_root_dir, package_id)
 
-            # Need to rename the container files so they don't get ID conflicts
-            to_rename_files = sub_dir_name not in ("plugins",)
-            self.__installPackageFiles(package_id, src_dir_path, dst_dir_path, need_to_rename_files= to_rename_files)
+                if not os.path.exists(src_dir_path):
+                    continue
 
-        archive.close()
+                # Need to rename the container files so they don't get ID conflicts
+                to_rename_files = sub_dir_name not in ("plugins",)
+                self.__installPackageFiles(package_id, src_dir_path, dst_dir_path, need_to_rename_files= to_rename_files)
 
         # Remove the file
         os.remove(filename)
@@ -321,33 +325,32 @@ class CuraPackageManager(QObject):
                 os.rename(old_file_path, new_file_path)
 
     # Gets package information from the given file.
-    def getPackageInfo(self, filename: str) -> dict:
-        archive = zipfile.ZipFile(filename, "r")
-        try:
-            # All information is in package.json
-            with archive.open("package.json", "r") as f:
-                package_info_dict = json.loads(f.read().decode("utf-8"))
-                return package_info_dict
-        except Exception as e:
-            raise RuntimeError("Could not get package information from file '%s': %s" % (filename, e))
-        finally:
-            archive.close()
+    def getPackageInfo(self, filename: str) -> Dict[str, Any]:
+        with zipfile.ZipFile(filename) as archive:
+            try:
+                # All information is in package.json
+                with archive.open("package.json") as f:
+                    package_info_dict = json.loads(f.read().decode("utf-8"))
+                    return package_info_dict
+            except Exception as e:
+                Logger.logException("w", "Could not get package information from file '%s': %s" % (filename, e))
+                return {}
 
     # Gets the license file content if present in the given package file.
     # Returns None if there is no license file found.
     def getPackageLicense(self, filename: str) -> Optional[str]:
         license_string = None
-        archive = zipfile.ZipFile(filename)
-        try:
+        with zipfile.ZipFile(filename) as archive:
             # Go through all the files and use the first successful read as the result
             for file_info in archive.infolist():
-                if file_info.is_dir() or not file_info.filename.startswith("files/"):
+                is_dir = lambda file_info: file_info.filename.endswith('/')
+                if is_dir or not file_info.filename.startswith("files/"):
                     continue
 
                 filename_parts = os.path.basename(file_info.filename.lower()).split(".")
                 stripped_filename = filename_parts[0]
                 if stripped_filename in ("license", "licence"):
-                    Logger.log("i", "Found potential license file '%s'", file_info.filename)
+                    Logger.log("d", "Found potential license file '%s'", file_info.filename)
                     try:
                         with archive.open(file_info.filename, "r") as f:
                             data = f.read()
@@ -357,8 +360,4 @@ class CuraPackageManager(QObject):
                         Logger.logException("e", "Failed to load potential license file '%s' as text file.",
                                             file_info.filename)
                         license_string = None
-        except Exception as e:
-            raise RuntimeError("Could not get package license from file '%s': %s" % (filename, e))
-        finally:
-            archive.close()
         return license_string

+ 1 - 1
cura/Machines/MaterialManager.py

@@ -220,7 +220,7 @@ class MaterialManager(QObject):
                 else:
                     # Add this container id to the wrong containers list in the registry
                     Logger.log("w", "Not adding {id} to the material manager because the variant does not exist.".format(id = material_metadata["id"]))
-                    self._container_registry.wrong_container_ids.append(material_metadata["id"])
+                    self._container_registry.addWrongContainerId(material_metadata["id"])
 
         self.materialsUpdated.emit()
 

+ 11 - 2
cura/PrintInformation.py

@@ -337,11 +337,20 @@ class PrintInformation(QObject):
         if is_gcode or is_project_file or (is_empty or (self._base_name == "" and self._base_name != name)):
             # Only take the file name part, Note : file name might have 'dot' in name as well
             if is_project_file:
+                # This is for .curaproject, loaded as project
                 self._base_name = ".".join(filename_parts)
             elif len(filename_parts) > 1:
-                self._base_name = ".".join(filename_parts[0:-1])
+                if "gcode" in filename_parts:
+                    gcode_index = filename_parts.index('gcode')
+                    self._base_name = ".".join(filename_parts[0:gcode_index])
+                elif "curaproject" in filename_parts:
+                    #load a project and import only models
+                    curaproject_index = filename_parts.index('curaproject')
+                    self._base_name = ".".join(filename_parts[0:curaproject_index])
+                else:
+                    self._base_name = name
             else:
-                self._base_name = filename_parts[0]
+                self._base_name = name
 
 
             self._updateJobName()

+ 54 - 49
cura/Settings/MachineManager.py

@@ -8,6 +8,8 @@ from typing import List, Dict, TYPE_CHECKING, Optional
 
 from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
 from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
+from UM.Settings.InstanceContainer import InstanceContainer
+from UM.Settings.Interfaces import ContainerInterface
 from UM.Signal import Signal
 
 from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QTimer
@@ -29,6 +31,7 @@ from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
 from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
 from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel
 from cura.Settings.ExtruderManager import ExtruderManager
+from cura.Settings.ExtruderStack import ExtruderStack
 
 from .CuraStackBuilder import CuraStackBuilder
 
@@ -39,16 +42,15 @@ if TYPE_CHECKING:
     from cura.Settings.CuraContainerStack import CuraContainerStack
     from cura.Settings.GlobalStack import GlobalStack
 
-
 class MachineManager(QObject):
 
     def __init__(self, parent = None):
         super().__init__(parent)
 
-        self._active_container_stack = None     # type: CuraContainerStack
-        self._global_container_stack = None     # type: GlobalStack
+        self._active_container_stack = None     # type: Optional[ExtruderManager]
+        self._global_container_stack = None     # type: Optional[GlobalStack]
 
-        self._current_root_material_id = {}
+        self._current_root_material_id = {}  # type: Dict[str, str]
         self._current_quality_group = None
         self._current_quality_changes_group = None
 
@@ -63,7 +65,7 @@ class MachineManager(QObject):
 
         self._application = Application.getInstance()
         self._application.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
-        self._application.getContainerRegistry().containerLoadComplete.connect(self._onInstanceContainersChanged)
+        self._application.getContainerRegistry().containerLoadComplete.connect(self._onContainersChanged)
 
         ##  When the global container is changed, active material probably needs to be updated.
         self.globalContainerChanged.connect(self.activeMaterialChanged)
@@ -100,7 +102,7 @@ class MachineManager(QObject):
 
         self._global_event_keys = set()
 
-        self._printer_output_devices = []
+        self._printer_output_devices = []  # type: List[PrinterOutputDevice]
         Application.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
         # There might already be some output devices by the time the signal is connected
         self._onOutputDevicesChanged()
@@ -161,7 +163,7 @@ class MachineManager(QObject):
 
     rootMaterialChanged = pyqtSignal()
 
-    def setInitialActiveMachine(self):
+    def setInitialActiveMachine(self) -> None:
         active_machine_id = Preferences.getInstance().getValue("cura/active_machine")
         if active_machine_id != "" and ContainerRegistry.getInstance().findContainerStacksMetadata(id = active_machine_id):
             # An active machine was saved, so restore it.
@@ -176,7 +178,7 @@ class MachineManager(QObject):
         self.outputDevicesChanged.emit()
 
     @pyqtProperty(QObject, notify = currentConfigurationChanged)
-    def currentConfiguration(self):
+    def currentConfiguration(self) -> ConfigurationModel:
         return self._current_printer_configuration
 
     def _onCurrentConfigurationChanged(self) -> None:
@@ -209,7 +211,7 @@ class MachineManager(QObject):
         return self._current_printer_configuration == configuration
 
     @pyqtProperty("QVariantList", notify = outputDevicesChanged)
-    def printerOutputDevices(self):
+    def printerOutputDevices(self) -> List[PrinterOutputDevice]:
         return self._printer_output_devices
 
     @pyqtProperty(int, constant=True)
@@ -223,7 +225,7 @@ class MachineManager(QObject):
             except TypeError:  # pyQtSignal gives a TypeError when disconnecting from something that was already disconnected.
                 pass
             try:
-                self._global_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged)
+                self._global_container_stack.containersChanged.disconnect(self._onContainersChanged)
             except TypeError:
                 pass
             try:
@@ -233,7 +235,7 @@ class MachineManager(QObject):
 
             for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks():
                 extruder_stack.propertyChanged.disconnect(self._onPropertyChanged)
-                extruder_stack.containersChanged.disconnect(self._onInstanceContainersChanged)
+                extruder_stack.containersChanged.disconnect(self._onContainersChanged)
 
         # Update the local global container stack reference
         self._global_container_stack = Application.getInstance().getGlobalContainerStack()
@@ -247,7 +249,7 @@ class MachineManager(QObject):
             Preferences.getInstance().setValue("cura/active_machine", self._global_container_stack.getId())
 
             self._global_container_stack.nameChanged.connect(self._onMachineNameChanged)
-            self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
+            self._global_container_stack.containersChanged.connect(self._onContainersChanged)
             self._global_container_stack.propertyChanged.connect(self._onPropertyChanged)
 
             # Global stack can have only a variant if it is a buildplate
@@ -264,7 +266,7 @@ class MachineManager(QObject):
             # Listen for changes on all extruder stacks
             for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks():
                 extruder_stack.propertyChanged.connect(self._onPropertyChanged)
-                extruder_stack.containersChanged.connect(self._onInstanceContainersChanged)
+                extruder_stack.containersChanged.connect(self._onContainersChanged)
 
             if self._global_container_stack.getId() in self.machine_extruder_material_update_dict:
                 for func in self.machine_extruder_material_update_dict[self._global_container_stack.getId()]:
@@ -291,7 +293,7 @@ class MachineManager(QObject):
 
         self.rootMaterialChanged.emit()
 
-    def _onInstanceContainersChanged(self, container) -> None:
+    def _onContainersChanged(self, container: ContainerInterface) -> None:
         self._instance_container_timer.start()
 
     def _onPropertyChanged(self, key: str, property_name: str) -> None:
@@ -300,7 +302,7 @@ class MachineManager(QObject):
             self.activeStackValueChanged.emit()
 
     ## Given a global_stack, make sure that it's all valid by searching for this quality group and applying it again
-    def _initMachineState(self, global_stack):
+    def _initMachineState(self, global_stack: "CuraContainerStack") -> None:
         material_dict = {}
         for position, extruder in global_stack.extruders.items():
             material_dict[position] = extruder.material.getMetaDataEntry("base_file")
@@ -623,7 +625,7 @@ class MachineManager(QObject):
 
     ## Copy the value of the setting of the current extruder to all other extruders as well as the global container.
     @pyqtSlot(str)
-    def copyValueToExtruders(self, key: str):
+    def copyValueToExtruders(self, key: str) -> None:
         new_value = self._active_container_stack.getProperty(key, "value")
         extruder_stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())]
 
@@ -634,7 +636,7 @@ class MachineManager(QObject):
 
     ## Copy the value of all manually changed settings of the current extruder to all other extruders.
     @pyqtSlot()
-    def copyAllValuesToExtruders(self):
+    def copyAllValuesToExtruders(self) -> None:
         extruder_stacks = list(self._global_container_stack.extruders.values())
         for extruder_stack in extruder_stacks:
             if extruder_stack != self._active_container_stack:
@@ -688,7 +690,7 @@ class MachineManager(QObject):
         return fallback_title
 
     @pyqtSlot(str, str)
-    def renameMachine(self, machine_id: str, new_name: str):
+    def renameMachine(self, machine_id: str, new_name: str) -> None:
         container_registry = ContainerRegistry.getInstance()
         machine_stack = container_registry.findContainerStacks(id = machine_id)
         if machine_stack:
@@ -697,7 +699,7 @@ class MachineManager(QObject):
             self.globalContainerChanged.emit()
 
     @pyqtSlot(str)
-    def removeMachine(self, machine_id: str):
+    def removeMachine(self, machine_id: str) -> None:
         # If the machine that is being removed is the currently active machine, set another machine as the active machine.
         activate_new_machine = (self._global_container_stack and self._global_container_stack.getId() == machine_id)
 
@@ -794,9 +796,9 @@ class MachineManager(QObject):
         if containers:
             return containers[0].definition.getId()
 
-    def getIncompatibleSettingsOnEnabledExtruders(self, container):
+    def getIncompatibleSettingsOnEnabledExtruders(self, container: InstanceContainer) -> List[str]:
         extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
-        result = []
+        result = []  # type: List[str]
         for setting_instance in container.findInstances():
             setting_key = setting_instance.definition.key
             setting_enabled = self._global_container_stack.getProperty(setting_key, "enabled")
@@ -818,7 +820,7 @@ class MachineManager(QObject):
         return result
 
     ##  Update extruder number to a valid value when the number of extruders are changed, or when an extruder is changed
-    def correctExtruderSettings(self):
+    def correctExtruderSettings(self) -> None:
         for setting_key in self.getIncompatibleSettingsOnEnabledExtruders(self._global_container_stack.userChanges):
             self._global_container_stack.userChanges.removeInstance(setting_key)
         add_user_changes = self.getIncompatibleSettingsOnEnabledExtruders(self._global_container_stack.qualityChanges)
@@ -835,7 +837,7 @@ class MachineManager(QObject):
 
     ##  Set the amount of extruders on the active machine (global stack)
     #   \param extruder_count int the number of extruders to set
-    def setActiveMachineExtruderCount(self, extruder_count):
+    def setActiveMachineExtruderCount(self, extruder_count: int) -> None:
         extruder_manager = Application.getInstance().getExtruderManager()
 
         definition_changes_container = self._global_container_stack.definitionChanges
@@ -890,13 +892,13 @@ class MachineManager(QObject):
         self.forceUpdateAllSettings()
 
     @pyqtSlot(int, result = QObject)
-    def getExtruder(self, position: int):
+    def getExtruder(self, position: int) -> Optional[ExtruderStack]:
         extruder = None
         if self._global_container_stack:
             extruder = self._global_container_stack.extruders.get(str(position))
         return extruder
 
-    def updateDefaultExtruder(self):
+    def updateDefaultExtruder(self) -> None:
         extruder_items = sorted(self._global_container_stack.extruders.items())
         old_position = self._default_extruder_position
         new_default_position = "0"
@@ -908,7 +910,7 @@ class MachineManager(QObject):
             self._default_extruder_position = new_default_position
             self.extruderChanged.emit()
 
-    def updateNumberExtrudersEnabled(self):
+    def updateNumberExtrudersEnabled(self) -> None:
         definition_changes_container = self._global_container_stack.definitionChanges
         machine_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
         extruder_count = 0
@@ -920,16 +922,16 @@ class MachineManager(QObject):
             self.numberExtrudersEnabledChanged.emit()
 
     @pyqtProperty(int, notify = numberExtrudersEnabledChanged)
-    def numberExtrudersEnabled(self):
+    def numberExtrudersEnabled(self) -> int:
         return self._global_container_stack.definitionChanges.getProperty("extruders_enabled_count", "value")
 
     @pyqtProperty(str, notify = extruderChanged)
-    def defaultExtruderPosition(self):
+    def defaultExtruderPosition(self) -> str:
         return self._default_extruder_position
 
     ##  This will fire the propertiesChanged for all settings so they will be updated in the front-end
     @pyqtSlot()
-    def forceUpdateAllSettings(self):
+    def forceUpdateAllSettings(self) -> None:
         with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
             property_names = ["value", "resolve", "validationState"]
             for container in [self._global_container_stack] + list(self._global_container_stack.extruders.values()):
@@ -937,8 +939,11 @@ class MachineManager(QObject):
                     container.propertiesChanged.emit(setting_key, property_names)
 
     @pyqtSlot(int, bool)
-    def setExtruderEnabled(self, position: int, enabled) -> None:
+    def setExtruderEnabled(self, position: int, enabled: bool) -> None:
         extruder = self.getExtruder(position)
+        if not extruder:
+            Logger.log("w", "Could not find extruder on position %s", position)
+
         extruder.setEnabled(enabled)
         self.updateDefaultExtruder()
         self.updateNumberExtrudersEnabled()
@@ -955,13 +960,13 @@ class MachineManager(QObject):
         # Also trigger the build plate compatibility to update
         self.activeMaterialChanged.emit()
 
-    def _onMachineNameChanged(self):
+    def _onMachineNameChanged(self) -> None:
         self.globalContainerChanged.emit()
 
-    def _onMaterialNameChanged(self):
+    def _onMaterialNameChanged(self) -> None:
         self.activeMaterialChanged.emit()
 
-    def _onQualityNameChanged(self):
+    def _onQualityNameChanged(self) -> None:
         self.activeQualityChanged.emit()
 
     def _getContainerChangedSignals(self) -> List[Signal]:
@@ -972,19 +977,19 @@ class MachineManager(QObject):
         return [ s.containersChanged for s in stacks ]
 
     @pyqtSlot(str, str, str)
-    def setSettingForAllExtruders(self, setting_name: str, property_name: str, property_value: str):
+    def setSettingForAllExtruders(self, setting_name: str, property_name: str, property_value: str) -> None:
         for key, extruder in self._global_container_stack.extruders.items():
             container = extruder.userChanges
             container.setProperty(setting_name, property_name, property_value)
 
     @pyqtProperty("QVariantList", notify = globalContainerChanged)
-    def currentExtruderPositions(self):
+    def currentExtruderPositions(self) -> List[str]:
         if self._global_container_stack is None:
             return []
         return sorted(list(self._global_container_stack.extruders.keys()))
 
     ##  Update _current_root_material_id when the current root material was changed.
-    def _onRootMaterialChanged(self):
+    def _onRootMaterialChanged(self) -> None:
         self._current_root_material_id = {}
 
         if self._global_container_stack:
@@ -992,13 +997,13 @@ class MachineManager(QObject):
                 self._current_root_material_id[position] = self._global_container_stack.extruders[position].material.getMetaDataEntry("base_file")
 
     @pyqtProperty("QVariant", notify = rootMaterialChanged)
-    def currentRootMaterialId(self):
+    def currentRootMaterialId(self) -> Dict[str, str]:
         return self._current_root_material_id
 
     ##  Return the variant names in the extruder stack(s).
     ##  For the variant in the global stack, use activeVariantBuildplateName
     @pyqtProperty("QVariant", notify = activeVariantChanged)
-    def activeVariantNames(self):
+    def activeVariantNames(self) -> Dict[str, str]:
         result = {}
 
         active_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
@@ -1014,7 +1019,7 @@ class MachineManager(QObject):
     # Sets all quality and quality_changes containers to empty_quality and empty_quality_changes containers
     # for all stacks in the currently active machine.
     #
-    def _setEmptyQuality(self):
+    def _setEmptyQuality(self) -> None:
         self._current_quality_group = None
         self._current_quality_changes_group = None
         self._global_container_stack.quality = self._empty_quality_container
@@ -1026,7 +1031,7 @@ class MachineManager(QObject):
         self.activeQualityGroupChanged.emit()
         self.activeQualityChangesGroupChanged.emit()
 
-    def _setQualityGroup(self, quality_group, empty_quality_changes = True):
+    def _setQualityGroup(self, quality_group, empty_quality_changes: bool = True) -> None:
         if quality_group.node_for_global.getContainer() is None:
             return
         for node in quality_group.nodes_for_extruders.values():
@@ -1222,7 +1227,7 @@ class MachineManager(QObject):
     ##  Given a printer definition name, select the right machine instance. In case it doesn't exist, create a new
     #   instance with the same network key.
     @pyqtSlot(str)
-    def switchPrinterType(self, machine_name):
+    def switchPrinterType(self, machine_name: str):
         # Don't switch if the user tries to change to the same type of printer
         if self.activeMachineDefinitionName == machine_name:
             return
@@ -1246,7 +1251,7 @@ class MachineManager(QObject):
         self.setActiveMachine(new_machine.getId())
 
     @pyqtSlot(QObject)
-    def applyRemoteConfiguration(self, configuration: ConfigurationModel):
+    def applyRemoteConfiguration(self, configuration: ConfigurationModel) -> None:
         self.blurSettings.emit()
         with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
             self.switchPrinterType(configuration.printerType)
@@ -1276,7 +1281,7 @@ class MachineManager(QObject):
             self._updateQualityWithMaterial()
 
     ##  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):
+    def replaceContainersMetadata(self, key: str, value: str, new_value: str) -> None:
         machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine")
         for machine in machines:
             if machine.getMetaDataEntry(key) == value:
@@ -1285,7 +1290,7 @@ class MachineManager(QObject):
     ##  This method checks if the name of the group stored in the definition container is correct.
     #   After updating from 3.2 to 3.3 some group names may be temporary. If there is a mismatch in the name of the group
     #   then all the container stacks are updated, both the current and the hidden ones.
-    def checkCorrectGroupName(self, device_id: str, group_name: str):
+    def checkCorrectGroupName(self, device_id: str, group_name: str) -> None:
         if self._global_container_stack and device_id == self.activeMachineNetworkKey:
             # Check if the connect_group_name is correct. If not, update all the containers connected to the same printer
             if self.activeMachineNetworkGroupName != group_name:
@@ -1319,7 +1324,7 @@ class MachineManager(QObject):
         self.setMaterial(position, material_node)
 
     @pyqtSlot(str, "QVariant")
-    def setMaterial(self, position, container_node):
+    def setMaterial(self, position: str, container_node) -> None:
         position = str(position)
         self.blurSettings.emit()
         with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
@@ -1327,13 +1332,13 @@ class MachineManager(QObject):
             self._updateQualityWithMaterial()
 
     @pyqtSlot(str, str)
-    def setVariantByName(self, position, variant_name):
+    def setVariantByName(self, position: str, variant_name: str) -> None:
         machine_definition_id = self._global_container_stack.definition.id
         variant_node = self._variant_manager.getVariantNode(machine_definition_id, variant_name)
         self.setVariant(position, variant_node)
 
     @pyqtSlot(str, "QVariant")
-    def setVariant(self, position, container_node):
+    def setVariant(self, position: str, container_node):
         position = str(position)
         self.blurSettings.emit()
         with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
@@ -1342,7 +1347,7 @@ class MachineManager(QObject):
             self._updateQualityWithMaterial()
 
     @pyqtSlot(str)
-    def setQualityGroupByQualityType(self, quality_type):
+    def setQualityGroupByQualityType(self, quality_type: str) -> None:
         if self._global_container_stack is None:
             return
         # Get all the quality groups for this global stack and filter out by quality_type
@@ -1394,7 +1399,7 @@ class MachineManager(QObject):
             name = self._current_quality_group.name
         return name
 
-    def _updateUponMaterialMetadataChange(self):
+    def _updateUponMaterialMetadataChange(self) -> None:
         if self._global_container_stack is None:
             return
         with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):

+ 2 - 0
plugins/Toolbox/resources/qml/Toolbox.qml

@@ -12,6 +12,8 @@ Window
     property var selection: null
     title: catalog.i18nc("@title", "Toolbox")
     modality: Qt.ApplicationModal
+    flags: Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint
+
     width: 720 * screenScaleFactor
     height: 640 * screenScaleFactor
     minimumWidth: 720 * screenScaleFactor

+ 6 - 4
plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml

@@ -1,7 +1,7 @@
 // Copyright (c) 2018 Ultimaker B.V.
 // Toolbox is released under the terms of the LGPLv3 or higher.
 
-import QtQuick 2.2
+import QtQuick 2.3
 import QtQuick.Controls 1.4
 import QtQuick.Controls.Styles 1.4
 import UM 1.1 as UM
@@ -9,7 +9,7 @@ import UM 1.1 as UM
 Item
 {
     id: page
-    property var details: base.selection
+    property var details: base.selection || {}
     anchors.fill: parent
     ToolboxBackColumn
     {
@@ -32,6 +32,7 @@ Item
             height: UM.Theme.getSize("toolbox_thumbnail_medium").height
             fillMode: Image.PreserveAspectFit
             source: details.icon_url || "../images/logobot.svg"
+            mipmap: true
             anchors
             {
                 top: parent.top
@@ -53,7 +54,7 @@ Item
                 rightMargin: UM.Theme.getSize("wide_margin").width
                 bottomMargin: UM.Theme.getSize("default_margin").height
             }
-            text: details.name
+            text: details.name || ""
             font: UM.Theme.getFont("large")
             wrapMode: Text.WordWrap
             width: parent.width
@@ -62,7 +63,7 @@ Item
         Label
         {
             id: description
-            text: details.description
+            text: details.description || ""
             anchors
             {
                 top: title.bottom
@@ -114,6 +115,7 @@ Item
                 }
                 font: UM.Theme.getFont("very_small")
                 color: UM.Theme.getColor("text")
+                linkColor: UM.Theme.getColor("text_link")
                 onLinkActivated: Qt.openUrlExternally(link)
             }
         }

+ 7 - 6
plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml

@@ -8,6 +8,7 @@ import UM 1.1 as UM
 
 Item
 {
+    property var packageData
     anchors.topMargin: UM.Theme.getSize("default_margin").height
     height: visible ? childrenRect.height : 0
     visible: packageData.type == "material" && packageData.has_configs
@@ -36,8 +37,8 @@ Item
             Label
             {
                 anchors.verticalCenter: parent.verticalCenter
-                elide: styleData.elideMode
-                text: styleData.value
+                elide: Text.ElideRight
+                text: styleData.value || ""
                 color: UM.Theme.getColor("text")
                 font: UM.Theme.getFont("default_bold")
             }
@@ -55,8 +56,8 @@ Item
             Label
             {
                 anchors.verticalCenter: parent.verticalCenter
-                elide: styleData.elideMode
-                text: styleData.value
+                elide: Text.ElideRight
+                text: styleData.value || ""
                 color: UM.Theme.getColor("text_medium")
                 font: UM.Theme.getFont("default")
             }
@@ -67,8 +68,8 @@ Item
             Label
             {
                 anchors.verticalCenter: parent.verticalCenter
-                elide: styleData.elideMode
-                text: styleData.value
+                elide: Text.ElideRight
+                text: styleData.value || ""
                 color: UM.Theme.getColor("text_medium")
                 font: UM.Theme.getFont("default")
             }

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