Browse Source

Merge branch 'master' into feature_headless_docker

ChrisTerBeke 7 years ago
parent
commit
422d047317

+ 10 - 13
cura/CuraApplication.py

@@ -300,6 +300,7 @@ class CuraApplication(QtApplication):
         empty_quality_changes_container = copy.deepcopy(empty_container)
         empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
         empty_quality_changes_container.addMetaDataEntry("type", "quality_changes")
+        empty_quality_changes_container.addMetaDataEntry("quality_type", "not_supported")
         ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
 
         with ContainerRegistry.getInstance().lockFile():
@@ -1082,9 +1083,10 @@ class CuraApplication(QtApplication):
             op.push()
             Selection.clear()
 
-        Logger.log("i", "Reseting print information")
-        self._print_information = PrintInformation.PrintInformation()
-        
+        # Reset the print information:
+        self.getController().getScene().sceneChanged.emit(node)
+        # self._print_information.setToZeroPrintInformation(self.getBuildPlateModel().activeBuildPlate)
+
         # stay on the same build plate
         #self.getCuraSceneController().setActiveBuildPlate(0)  # Select first build plate
 
@@ -1484,11 +1486,7 @@ class CuraApplication(QtApplication):
 
             extension = os.path.splitext(filename)[1]
             if extension.lower() in self._non_sliceable_extensions:
-                self.getController().setActiveView("SimulationView")
-                view = self.getController().getActiveView()
-                view.resetLayerData()
-                view.setLayer(9999999)
-                view.calculateMaxLayers()
+                self.callLater(lambda: self.getController().setActiveView("SimulationView"))
 
                 block_slicing_decorator = BlockSlicingDecorator()
                 node.addDecorator(block_slicing_decorator)
@@ -1544,12 +1542,11 @@ class CuraApplication(QtApplication):
         """
         Checks if the given file URL is a valid project file.
         """
+        file_path = QUrl(file_url).toLocalFile()
+        workspace_reader = self.getWorkspaceFileHandler().getReaderForFile(file_path)
+        if workspace_reader is None:
+            return False  # non-project files won't get a reader
         try:
-            file_path = QUrl(file_url).toLocalFile()
-            workspace_reader = self.getWorkspaceFileHandler().getReaderForFile(file_path)
-            if workspace_reader is None:
-                return False  # non-project files won't get a reader
-
             result = workspace_reader.preRead(file_path, show_dialog=False)
             return result == WorkspaceReader.PreReadResult.accepted
         except Exception as e:

+ 3 - 2
cura/PrintInformation.py

@@ -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.
 
 from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty
@@ -65,6 +65,7 @@ class PrintInformation(QObject):
         self._backend = Application.getInstance().getBackend()
         if self._backend:
             self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
+        Application.getInstance().getController().getScene().sceneChanged.connect(self.setToZeroPrintInformation)
 
         self._base_name = ""
         self._abbr_machine = ""
@@ -171,7 +172,7 @@ class PrintInformation(QObject):
     def printTimes(self):
         return self._print_time_message_values[self._active_build_plate]
 
-    def _onPrintDurationMessage(self, build_plate_number, print_time, material_amounts):
+    def _onPrintDurationMessage(self, build_plate_number, print_time: Dict[str, int], material_amounts: list):
         self._updateTotalPrintTimePerFeature(build_plate_number, print_time)
         self.currentPrintTimeChanged.emit()
 

+ 0 - 1
cura/PrinterOutputDevice.py

@@ -135,7 +135,6 @@ class PrinterOutputDevice(QObject, OutputDevice):
     def controlItem(self):
         if not self._control_component:
             self._createControlViewFromQML()
-
         return self._control_item
 
     def _createControlViewFromQML(self):

+ 0 - 3
cura/QualityManager.py

@@ -136,9 +136,6 @@ class QualityManager:
             if basic_materials:
                 result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria)
 
-        empty_quality = ContainerRegistry.getInstance().findInstanceContainers(id = "empty_quality")[0]
-        result.append(empty_quality)
-
         return result
 
     ##  Find all quality changes for a machine.

+ 1 - 1
cura/Settings/CuraContainerRegistry.py

@@ -94,7 +94,7 @@ class CuraContainerRegistry(ContainerRegistry):
     def _containerExists(self, container_type, container_name):
         container_class = ContainerStack if container_type == "machine" else InstanceContainer
 
-        return self.findContainersMetadata(id = container_name, type = container_type, ignore_case = True) or \
+        return self.findContainersMetadata(container_type = container_class, id = container_name, type = container_type, ignore_case = True) or \
                 self.findContainersMetadata(container_type = container_class, name = container_name, type = container_type)
 
     ##  Exports an profile to a file

+ 54 - 9
cura/Settings/CuraContainerStack.py

@@ -144,7 +144,7 @@ class CuraContainerStack(ContainerStack):
 
     ##  Set the material container.
     #
-    #   \param new_quality_changes The new material container. It is expected to have a "type" metadata entry with the value "quality_changes".
+    #   \param new_material The new material container. It is expected to have a "type" metadata entry with the value "material".
     def setMaterial(self, new_material: InstanceContainer, postpone_emit = False) -> None:
         self.replaceContainer(_ContainerIndexes.Material, new_material, postpone_emit = postpone_emit)
 
@@ -155,7 +155,7 @@ class CuraContainerStack(ContainerStack):
     #   to whatever the machine definition specifies as "preferred" container, or a fallback value. See findDefaultMaterial
     #   for details.
     #
-    #   \param new_quality_changes_id The ID of the new material container.
+    #   \param new_material_id The ID of the new material container.
     #
     #   \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
     def setMaterialById(self, new_material_id: str) -> None:
@@ -182,7 +182,7 @@ class CuraContainerStack(ContainerStack):
 
     ##  Set the variant container.
     #
-    #   \param new_quality_changes The new variant container. It is expected to have a "type" metadata entry with the value "quality_changes".
+    #   \param new_variant The new variant container. It is expected to have a "type" metadata entry with the value "variant".
     def setVariant(self, new_variant: InstanceContainer) -> None:
         self.replaceContainer(_ContainerIndexes.Variant, new_variant)
 
@@ -193,13 +193,13 @@ class CuraContainerStack(ContainerStack):
     #   to whatever the machine definition specifies as "preferred" container, or a fallback value. See findDefaultVariant
     #   for details.
     #
-    #   \param new_quality_changes_id The ID of the new variant container.
+    #   \param new_variant_id The ID of the new variant container.
     #
     #   \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
     def setVariantById(self, new_variant_id: str) -> None:
         variant = self._empty_variant
         if new_variant_id == "default":
-            new_variant = self.findDefaultVariant()
+            new_variant = self.findDefaultVariantBuildplate() if self.getMetaDataEntry("type") == "machine" else self.findDefaultVariant()
             if new_variant:
                 variant = new_variant
         else:
@@ -220,13 +220,13 @@ class CuraContainerStack(ContainerStack):
 
     ##  Set the definition changes container.
     #
-    #   \param new_quality_changes The new definition changes container. It is expected to have a "type" metadata entry with the value "quality_changes".
+    #   \param new_definition_changes The new definition changes container. It is expected to have a "type" metadata entry with the value "definition_changes".
     def setDefinitionChanges(self, new_definition_changes: InstanceContainer) -> None:
         self.replaceContainer(_ContainerIndexes.DefinitionChanges, new_definition_changes)
 
     ##  Set the definition changes container by an ID.
     #
-    #   \param new_quality_changes_id The ID of the new definition changes container.
+    #   \param new_definition_changes_id The ID of the new definition changes container.
     #
     #   \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
     def setDefinitionChangesById(self, new_definition_changes_id: str) -> None:
@@ -245,13 +245,13 @@ class CuraContainerStack(ContainerStack):
 
     ##  Set the definition container.
     #
-    #   \param new_quality_changes The new definition container. It is expected to have a "type" metadata entry with the value "quality_changes".
+    #   \param new_definition The new definition container. It is expected to have a "type" metadata entry with the value "definition".
     def setDefinition(self, new_definition: DefinitionContainerInterface) -> None:
         self.replaceContainer(_ContainerIndexes.Definition, new_definition)
 
     ##  Set the definition container by an ID.
     #
-    #   \param new_quality_changes_id The ID of the new definition container.
+    #   \param new_definition_id The ID of the new definition container.
     #
     #   \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
     def setDefinitionById(self, new_definition_id: str) -> None:
@@ -435,6 +435,51 @@ class CuraContainerStack(ContainerStack):
         Logger.log("w", "Could not find a valid default variant for stack {stack}", stack = self.id)
         return None
 
+    ##  Find the global variant that should be used as "default". This is used for the buildplates.
+    #
+    #   This will search for variants that match the current definition and pick the preferred one,
+    #   if specified by the machine definition.
+    #
+    #   The following criteria are used to find the default global variant:
+    #   - If the machine definition does not have a metadata entry "has_variant_buildplates" set to True, return None
+    #   - The definition of the variant should be the same as the machine definition for this stack.
+    #   - The container should have a metadata entry "type" with value "variant" and "hardware_type" with value "buildplate".
+    #   - If the machine definition has a metadata entry "preferred_variant_buildplate", filter the variant IDs based on that.
+    #
+    #   \return The container that should be used as default, or None if nothing was found or the machine does not use variants.
+    #
+    #   \note This method assumes the stack has a valid machine definition.
+    def findDefaultVariantBuildplate(self) -> Optional[ContainerInterface]:
+        definition = self._getMachineDefinition()
+        # has_variant_buildplates can be overridden in other containers and stacks.
+        # In the case of UM2, it is overridden in the GlobalStack
+        if not self.getMetaDataEntry("has_variant_buildplates"):
+            # If the machine does not use variants, we should never set a variant.
+            return None
+
+        # First add any variant. Later, overwrite with preference if the preference is valid.
+        variant = None
+        definition_id = self._findInstanceContainerDefinitionId(definition)
+        variants = ContainerRegistry.getInstance().findInstanceContainers(definition = definition_id, type = "variant", hardware_type = "buildplate")
+        if variants:
+            variant = variants[0]
+
+        preferred_variant_buildplate_id = definition.getMetaDataEntry("preferred_variant_buildplate")
+        if preferred_variant_buildplate_id:
+            preferred_variant_buildplates = ContainerRegistry.getInstance().findInstanceContainers(id = preferred_variant_buildplate_id, definition = definition_id, type = "variant")
+            if preferred_variant_buildplates:
+                variant = preferred_variant_buildplates[0]
+            else:
+                Logger.log("w", "The preferred variant buildplate \"{variant}\" of stack {stack} does not exist or is not a variant.",
+                           variant = preferred_variant_buildplate_id, stack = self.id)
+                # And leave it at the default variant.
+
+        if variant:
+            return variant
+
+        Logger.log("w", "Could not find a valid default buildplate variant for stack {stack}", stack = self.id)
+        return None
+
     ##  Find the material that should be used as "default" material.
     #
     #   This will search for materials that match the current definition and pick the preferred one,

+ 4 - 0
cura/Settings/ExtruderManager.py

@@ -49,6 +49,9 @@ class ExtruderManager(QObject):
     ##  Notify when the user switches the currently active extruder.
     activeExtruderChanged = pyqtSignal()
 
+    ## The signal notifies subscribers if extruders are added
+    extrudersAdded = pyqtSignal()
+
     ##  Gets the unique identifier of the currently active extruder stack.
     #
     #   The currently active extruder stack is the stack that is currently being
@@ -406,6 +409,7 @@ class ExtruderManager(QObject):
 
             if extruders_changed:
                 self.extrudersChanged.emit(global_stack_id)
+                self.extrudersAdded.emit()
                 self.setActiveExtruderIndex(0)
 
     ##  Get all extruder values for a certain setting.

+ 95 - 3
cura/Settings/MachineManager.py

@@ -50,6 +50,7 @@ class MachineManager(QObject):
 
         # Used to store the new containers until after confirming the dialog
         self._new_variant_container = None
+        self._new_buildplate_container = None
         self._new_material_container = None
         self._new_quality_containers = []
 
@@ -157,6 +158,10 @@ class MachineManager(QObject):
     def newVariant(self):
         return self._new_variant_container
 
+    @property
+    def newBuildplate(self):
+        return self._new_buildplate_container
+
     @property
     def newMaterial(self):
         return self._new_material_container
@@ -309,10 +314,11 @@ class MachineManager(QObject):
             self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
             self._global_container_stack.propertyChanged.connect(self._onPropertyChanged)
 
-            # set the global variant to empty as we now use the extruder stack at all times - CURA-4482
+            # Global stack can have only a variant if it is a buildplate
             global_variant = self._global_container_stack.variant
             if global_variant != self._empty_variant_container:
-                self._global_container_stack.setVariant(self._empty_variant_container)
+                if global_variant.getMetaDataEntry("hardware_type") != "buildplate":
+                    self._global_container_stack.setVariant(self._empty_variant_container)
 
             # set the global material to empty as we now use the extruder stack at all times - CURA-4482
             global_material = self._global_container_stack.material
@@ -675,6 +681,14 @@ class MachineManager(QObject):
                 return quality.getId()
         return ""
 
+    @pyqtProperty(str, notify=activeVariantChanged)
+    def globalVariantId(self) -> str:
+        if self._global_container_stack:
+            variant = self._global_container_stack.variant
+            if variant and not isinstance(variant, type(self._empty_variant_container)):
+                return variant.getId()
+        return ""
+
     @pyqtProperty(str, notify = activeQualityChanged)
     def activeQualityType(self) -> str:
         if self._active_container_stack:
@@ -855,6 +869,24 @@ class MachineManager(QObject):
             else:
                 Logger.log("w", "While trying to set the active variant, no variant was found to replace.")
 
+    @pyqtSlot(str)
+    def setActiveVariantBuildplate(self, variant_buildplate_id: str):
+        with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
+            containers = ContainerRegistry.getInstance().findInstanceContainers(id = variant_buildplate_id)
+            if not containers or not self._global_container_stack:
+                return
+            Logger.log("d", "Attempting to change the active buildplate to %s", variant_buildplate_id)
+            old_buildplate = self._global_container_stack.variant
+            if old_buildplate:
+                self.blurSettings.emit()
+                self._new_buildplate_container = containers[0]  # self._active_container_stack will be updated with a delay
+                Logger.log("d", "Active buildplate changed to {active_variant_buildplate_id}".format(active_variant_buildplate_id = containers[0].getId()))
+
+                # Force set the active quality as it is so the values are updated
+                self.setActiveMaterial(self._active_container_stack.material.getId())
+            else:
+                Logger.log("w", "While trying to set the active buildplate, no buildplate was found to replace.")
+
     ##  set the active quality
     #   \param quality_id The quality_id of either a quality or a quality_changes
     @pyqtSlot(str)
@@ -939,6 +971,10 @@ class MachineManager(QObject):
             self._active_container_stack.variant = self._new_variant_container
             self._new_variant_container = None
 
+        if self._new_buildplate_container is not None:
+            self._global_container_stack.variant = self._new_buildplate_container
+            self._new_buildplate_container = None
+
         if self._new_material_container is not None:
             self._active_container_stack.material = self._new_material_container
             self._new_material_container = None
@@ -961,6 +997,7 @@ class MachineManager(QObject):
     #   Used for ignoring any changes when switching between printers (setActiveMachine)
     def _cancelDelayedActiveContainerStackChanges(self):
         self._new_material_container = None
+        self._new_buildplate_container = None
         self._new_variant_container = None
 
     ##  Determine the quality and quality changes settings for the current machine for a quality name.
@@ -1125,6 +1162,15 @@ class MachineManager(QObject):
 
         return ""
 
+    @pyqtProperty(str, notify = activeVariantChanged)
+    def activeVariantBuildplateName(self) -> str:
+        if self._global_container_stack:
+            variant = self._global_container_stack.variant
+            if variant:
+                return variant.getName()
+
+        return ""
+
     @pyqtProperty(str, notify = globalContainerChanged)
     def activeDefinitionId(self) -> str:
         if self._global_container_stack:
@@ -1222,7 +1268,6 @@ class MachineManager(QObject):
     def hasMaterials(self) -> bool:
         if self._global_container_stack:
             return Util.parseBool(self._global_container_stack.getMetaDataEntry("has_materials", False))
-
         return False
 
     @pyqtProperty(bool, notify = globalContainerChanged)
@@ -1231,6 +1276,53 @@ class MachineManager(QObject):
             return Util.parseBool(self._global_container_stack.getMetaDataEntry("has_variants", False))
         return False
 
+    @pyqtProperty(bool, notify = globalContainerChanged)
+    def hasVariantBuildplates(self) -> bool:
+        if self._global_container_stack:
+            return Util.parseBool(self._global_container_stack.getMetaDataEntry("has_variant_buildplates", False))
+        return False
+
+    ##  The selected buildplate is compatible if it is compatible with all the materials in all the extruders
+    @pyqtProperty(bool, notify = activeMaterialChanged)
+    def variantBuildplateCompatible(self) -> bool:
+        if not self._global_container_stack:
+            return True
+
+        buildplate_compatible = True  # It is compatible by default
+        extruder_stacks = self._global_container_stack.extruders.values()
+        for stack in extruder_stacks:
+            material_container = stack.material
+            if material_container == self._empty_material_container:
+                continue
+            if material_container.getMetaDataEntry("buildplate_compatible"):
+                buildplate_compatible = buildplate_compatible and material_container.getMetaDataEntry("buildplate_compatible")[self.activeVariantBuildplateName]
+
+        return buildplate_compatible
+
+    ##  The selected buildplate is usable if it is usable for all materials OR it is compatible for one but not compatible
+    #   for the other material but the buildplate is still usable
+    @pyqtProperty(bool, notify = activeMaterialChanged)
+    def variantBuildplateUsable(self) -> bool:
+        if not self._global_container_stack:
+            return True
+
+        # Here the next formula is being calculated:
+        # result = (not (material_left_compatible and material_right_compatible)) and
+        #           (material_left_compatible or material_left_usable) and
+        #           (material_right_compatible or material_right_usable)
+        result = not self.variantBuildplateCompatible
+        extruder_stacks = self._global_container_stack.extruders.values()
+        for stack in extruder_stacks:
+            material_container = stack.material
+            if material_container == self._empty_material_container:
+                continue
+            buildplate_compatible = material_container.getMetaDataEntry("buildplate_compatible")[self.activeVariantBuildplateName] if material_container.getMetaDataEntry("buildplate_compatible") else True
+            buildplate_usable = material_container.getMetaDataEntry("buildplate_recommended")[self.activeVariantBuildplateName] if material_container.getMetaDataEntry("buildplate_recommended") else True
+
+            result = result and (buildplate_compatible or buildplate_usable)
+
+        return result
+
     ##  Property to indicate if a machine has "specialized" material profiles.
     #   Some machines have their own material profiles that "override" the default catch all profiles.
     @pyqtProperty(bool, notify = globalContainerChanged)

+ 1 - 1
cura/Settings/ProfilesModel.py

@@ -87,7 +87,7 @@ class ProfilesModel(InstanceContainersModel):
             if quality.getMetaDataEntry("quality_type") not in quality_type_set:
                 result.append(quality)
 
-        if len(result) > 1:
+        if len(result) > 1 and self._empty_quality in result:
             result.remove(self._empty_quality)
 
         return {item.getId(): item for item in result}, {} #Only return true profiles for now, no metadata. The quality manager is not able to get only metadata yet.

+ 7 - 1
cura/Settings/QualityAndUserProfilesModel.py

@@ -1,17 +1,21 @@
 # Copyright (c) 2016 Ultimaker B.V.
 # Cura is released under the terms of the LGPLv3 or higher.
 from UM.Application import Application
+from UM.Settings.ContainerRegistry import ContainerRegistry
 
 from cura.QualityManager import QualityManager
 from cura.Settings.ProfilesModel import ProfilesModel
 from cura.Settings.ExtruderManager import ExtruderManager
 
+
 ##  QML Model for listing the current list of valid quality and quality changes profiles.
 #
 class QualityAndUserProfilesModel(ProfilesModel):
     def __init__(self, parent = None):
         super().__init__(parent)
 
+        self._empty_quality = ContainerRegistry.getInstance().findInstanceContainers(id = "empty_quality")[0]
+
     ##  Fetch the list of containers to display.
     #
     #   See UM.Settings.Models.InstanceContainersModel._fetchInstanceContainers().
@@ -35,7 +39,9 @@ class QualityAndUserProfilesModel(ProfilesModel):
 
         # Filter the quality_change by the list of available quality_types
         quality_type_set = set([x.getMetaDataEntry("quality_type") for x in quality_list])
-        filtered_quality_changes = {qc.getId():qc for qc in quality_changes_list if
+        # Also show custom profiles based on "Not Supported" quality profile
+        quality_type_set.add(self._empty_quality.getMetaDataEntry("quality_type"))
+        filtered_quality_changes = {qc.getId(): qc for qc in quality_changes_list if
                                     qc.getMetaDataEntry("quality_type") in quality_type_set and
                                     qc.getMetaDataEntry("extruder") is not None and
                                     (qc.getMetaDataEntry("extruder") == active_extruder.definition.getMetaDataEntry("quality_definition") or

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