Browse Source

Merge branch 'ui_rework_4_0' into CURA-5876-Configuration_dropdown

Conflicts:
	cura/Settings/MachineManager.py -> rowCount vs. count
	resources/qml/ExtruderIcon.qml -> Someone changed stuff that I had overwritten.
	resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml -> Someone changed stuff that I had removed.
	resources/qml/Toolbar.qml -> Git was wrong, not a conflict.
	resources/themes/cura-light/theme.json -> Git was wrong, not a conflict.
Ghostkeeper 6 years ago
parent
commit
7df4c01814

+ 8 - 6
cura/CuraActions.py

@@ -3,7 +3,7 @@
 
 from PyQt5.QtCore import QObject, QUrl
 from PyQt5.QtGui import QDesktopServices
-from typing import List, TYPE_CHECKING
+from typing import List, TYPE_CHECKING, cast
 
 from UM.Event import CallFunctionEvent
 from UM.FlameProfiler import pyqtSlot
@@ -61,8 +61,10 @@ class CuraActions(QObject):
         operation = GroupedOperation()
         for node in Selection.getAllSelectedObjects():
             current_node = node
-            while current_node.getParent() and current_node.getParent().callDecoration("isGroup"):
-                current_node = current_node.getParent()
+            parent_node = current_node.getParent()
+            while parent_node and parent_node.callDecoration("isGroup"):
+                current_node = parent_node
+                parent_node = current_node.getParent()
 
             #   This was formerly done with SetTransformOperation but because of
             #   unpredictable matrix deconstruction it was possible that mirrors
@@ -150,13 +152,13 @@ class CuraActions(QObject):
 
         root = cura.CuraApplication.CuraApplication.getInstance().getController().getScene().getRoot()
 
-        nodes_to_change = []
+        nodes_to_change = []  # type: List[SceneNode]
         for node in Selection.getAllSelectedObjects():
             parent_node = node  # Find the parent node to change instead
             while parent_node.getParent() != root:
-                parent_node = parent_node.getParent()
+                parent_node = cast(SceneNode, parent_node.getParent())
 
-            for single_node in BreadthFirstIterator(parent_node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
+            for single_node in BreadthFirstIterator(parent_node):  # type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
                 nodes_to_change.append(single_node)
 
         if not nodes_to_change:

+ 3 - 1
cura/CuraApplication.py

@@ -1663,7 +1663,9 @@ class CuraApplication(QtApplication):
             is_non_sliceable = "." + file_extension in self._non_sliceable_extensions
 
             if is_non_sliceable:
-                self.callLater(lambda: self.getController().setActiveView("SimulationView"))
+                # Need to switch first to the preview stage and then to layer view
+                self.callLater(lambda: (self.getController().setActiveStage("PreviewStage"),
+                                        self.getController().setActiveView("SimulationView")))
 
                 block_slicing_decorator = BlockSlicingDecorator()
                 node.addDecorator(block_slicing_decorator)

+ 4 - 1
cura/Machines/Models/QualityProfilesDropDownMenuModel.py

@@ -21,6 +21,7 @@ class QualityProfilesDropDownMenuModel(ListModel):
     AvailableRole = Qt.UserRole + 5
     QualityGroupRole = Qt.UserRole + 6
     QualityChangesGroupRole = Qt.UserRole + 7
+    IsExperimentalRole = Qt.UserRole + 8
 
     def __init__(self, parent = None):
         super().__init__(parent)
@@ -32,6 +33,7 @@ class QualityProfilesDropDownMenuModel(ListModel):
         self.addRoleName(self.AvailableRole, "available") #Whether the quality profile is available in our current nozzle + material.
         self.addRoleName(self.QualityGroupRole, "quality_group")
         self.addRoleName(self.QualityChangesGroupRole, "quality_changes_group")
+        self.addRoleName(self.IsExperimentalRole, "is_experimental")
 
         self._application = Application.getInstance()
         self._machine_manager = self._application.getMachineManager()
@@ -74,7 +76,8 @@ class QualityProfilesDropDownMenuModel(ListModel):
                     "layer_height": layer_height,
                     "layer_height_unit": self._layer_height_unit,
                     "available": quality_group.is_available,
-                    "quality_group": quality_group}
+                    "quality_group": quality_group,
+                    "is_experimental": quality_group.is_experimental}
 
             item_list.append(item)
 

+ 18 - 0
cura/Machines/QualityGroup.py

@@ -4,6 +4,9 @@
 from typing import Dict, Optional, List, Set
 
 from PyQt5.QtCore import QObject, pyqtSlot
+
+from UM.Util import parseBool
+
 from cura.Machines.ContainerNode import ContainerNode
 
 
@@ -29,6 +32,7 @@ class QualityGroup(QObject):
         self.nodes_for_extruders = {}  # type: Dict[int, ContainerNode]
         self.quality_type = quality_type
         self.is_available = False
+        self.is_experimental = False
 
     @pyqtSlot(result = str)
     def getName(self) -> str:
@@ -51,3 +55,17 @@ class QualityGroup(QObject):
         for extruder_node in self.nodes_for_extruders.values():
             result.append(extruder_node)
         return result
+
+    def setGlobalNode(self, node: "ContainerNode") -> None:
+        self.node_for_global = node
+
+        # Update is_experimental flag
+        is_experimental = parseBool(node.getMetaDataEntry("is_experimental", False))
+        self.is_experimental |= is_experimental
+
+    def setExtruderNode(self, position: int, node: "ContainerNode") -> None:
+        self.nodes_for_extruders[position] = node
+
+        # Update is_experimental flag
+        is_experimental = parseBool(node.getMetaDataEntry("is_experimental", False))
+        self.is_experimental |= is_experimental

+ 3 - 3
cura/Machines/QualityManager.py

@@ -235,7 +235,7 @@ class QualityManager(QObject):
 
                 for quality_type, quality_node in node.quality_type_map.items():
                     quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type)
-                    quality_group.node_for_global = quality_node
+                    quality_group.setGlobalNode(quality_node)
                     quality_group_dict[quality_type] = quality_group
                 break
 
@@ -337,7 +337,7 @@ class QualityManager(QObject):
 
                         quality_group = quality_group_dict[quality_type]
                         if position not in quality_group.nodes_for_extruders:
-                            quality_group.nodes_for_extruders[position] = quality_node
+                            quality_group.setExtruderNode(position, quality_node)
 
                 # If the machine has its own specific qualities, for extruders, it should skip the global qualities
                 # and use the material/variant specific qualities.
@@ -367,7 +367,7 @@ class QualityManager(QObject):
             if node and node.quality_type_map:
                 for quality_type, quality_node in node.quality_type_map.items():
                     quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type)
-                    quality_group.node_for_global = quality_node
+                    quality_group.setGlobalNode(quality_node)
                     quality_group_dict[quality_type] = quality_group
                 break
 

+ 1 - 1
cura/Machines/VariantManager.py

@@ -107,7 +107,7 @@ class VariantManager:
                     break
             return variant_node
 
-        return self._machine_to_variant_dict_map[machine_definition_id].get(variant_type, {}).get(variant_name)
+        return self._machine_to_variant_dict_map.get(machine_definition_id, {}).get(variant_type, {}).get(variant_name)
 
     def getVariantNodes(self, machine: "GlobalStack", variant_type: "VariantType") -> Dict[str, ContainerNode]:
         machine_definition_id = machine.definition.getId()

+ 3 - 4
cura/PrintInformation.py

@@ -14,8 +14,7 @@ from UM.Logger import Logger
 from UM.Qt.Duration import Duration
 from UM.Scene.SceneNode import SceneNode
 from UM.i18n import i18nCatalog
-from UM.MimeTypeDatabase import MimeTypeDatabase
-
+from UM.MimeTypeDatabase import MimeTypeDatabase, MimeTypeNotFoundError
 
 from typing import TYPE_CHECKING
 
@@ -361,7 +360,7 @@ class PrintInformation(QObject):
             try:
                 mime_type = MimeTypeDatabase.getMimeTypeForFile(name)
                 data = mime_type.stripExtension(name)
-            except:
+            except MimeTypeNotFoundError:
                 Logger.log("w", "Unsupported Mime Type Database file extension %s", name)
 
             if data is not None and check_name is not None:
@@ -402,7 +401,7 @@ class PrintInformation(QObject):
         return ''.join(char for char in unicodedata.normalize('NFD', to_strip) if unicodedata.category(char) != 'Mn')
 
     @pyqtSlot(result = "QVariantMap")
-    def getFeaturePrintTimes(self):
+    def getFeaturePrintTimes(self) -> Dict[str, Duration]:
         result = {}
         if self._active_build_plate not in self._print_times_per_feature:
             self._initPrintTimesPerFeature(self._active_build_plate)

+ 10 - 3
cura/Scene/ConvexHullNode.py

@@ -1,7 +1,10 @@
 # Copyright (c) 2015 Ultimaker B.V.
 # Cura is released under the terms of the LGPLv3 or higher.
+from typing import Optional
 
 from UM.Application import Application
+from UM.Math.Polygon import Polygon
+from UM.Qt.QtApplication import QtApplication
 from UM.Scene.SceneNode import SceneNode
 from UM.Resources import Resources
 from UM.Math.Color import Color
@@ -16,7 +19,7 @@ class ConvexHullNode(SceneNode):
     #   location an object uses on the buildplate. This area (or area's in case of one at a time printing) is
     #   then displayed as a transparent shadow. If the adhesion type is set to raft, the area is extruded
     #   to represent the raft as well.
-    def __init__(self, node, hull, thickness, parent = None):
+    def __init__(self, node: SceneNode, hull: Optional[Polygon], thickness: float, parent: Optional[SceneNode] = None) -> None:
         super().__init__(parent)
 
         self.setCalculateBoundingBox(False)
@@ -25,7 +28,11 @@ class ConvexHullNode(SceneNode):
 
         # Color of the drawn convex hull
         if not Application.getInstance().getIsHeadLess():
-            self._color = Color(*Application.getInstance().getTheme().getColor("convex_hull").getRgb())
+            theme = QtApplication.getInstance().getTheme()
+            if theme:
+                self._color = Color(*theme.getColor("convex_hull").getRgb())
+            else:
+                self._color = Color(0, 0, 0)
         else:
             self._color = Color(0, 0, 0)
 
@@ -75,7 +82,7 @@ class ConvexHullNode(SceneNode):
 
         return True
 
-    def _onNodeDecoratorsChanged(self, node):
+    def _onNodeDecoratorsChanged(self, node: SceneNode) -> None:
         convex_hull_head = self._node.callDecoration("getConvexHullHead")
         if convex_hull_head:
             convex_hull_head_builder = MeshBuilder()

+ 41 - 28
cura/Settings/MachineManager.py

@@ -66,7 +66,7 @@ class MachineManager(QObject):
 
         self.machine_extruder_material_update_dict = collections.defaultdict(list) #type: Dict[str, List[Callable[[], None]]]
 
-        self._instance_container_timer = QTimer() #type: QTimer
+        self._instance_container_timer = QTimer()  # type: QTimer
         self._instance_container_timer.setInterval(250)
         self._instance_container_timer.setSingleShot(True)
         self._instance_container_timer.timeout.connect(self.__emitChangedSignals)
@@ -76,7 +76,7 @@ class MachineManager(QObject):
         self._application.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
         self._container_registry.containerLoadComplete.connect(self._onContainersChanged)
 
-        ##  When the global container is changed, active material probably needs to be updated.
+        #  When the global container is changed, active material probably needs to be updated.
         self.globalContainerChanged.connect(self.activeMaterialChanged)
         self.globalContainerChanged.connect(self.activeVariantChanged)
         self.globalContainerChanged.connect(self.activeQualityChanged)
@@ -117,15 +117,15 @@ class MachineManager(QObject):
 
         self._material_incompatible_message = Message(catalog.i18nc("@info:status",
                                                 "The selected material is incompatible with the selected machine or configuration."),
-                                                title = catalog.i18nc("@info:title", "Incompatible Material")) #type: Message
+                                                title = catalog.i18nc("@info:title", "Incompatible Material"))  # type: Message
 
-        containers = CuraContainerRegistry.getInstance().findInstanceContainers(id = self.activeMaterialId) #type: List[InstanceContainer]
+        containers = CuraContainerRegistry.getInstance().findInstanceContainers(id = self.activeMaterialId)  # type: List[InstanceContainer]
         if containers:
             containers[0].nameChanged.connect(self._onMaterialNameChanged)
 
-        self._material_manager = self._application.getMaterialManager() #type: MaterialManager
-        self._variant_manager = self._application.getVariantManager() #type: VariantManager
-        self._quality_manager = self._application.getQualityManager() #type: QualityManager
+        self._material_manager = self._application.getMaterialManager()  # type: MaterialManager
+        self._variant_manager = self._application.getVariantManager()  # type: VariantManager
+        self._quality_manager = self._application.getQualityManager()  # type: QualityManager
 
         # When the materials lookup table gets updated, it can mean that a material has its name changed, which should
         # be reflected on the GUI. This signal emission makes sure that it happens.
@@ -158,7 +158,7 @@ class MachineManager(QObject):
     blurSettings = pyqtSignal()  # Emitted to force fields in the advanced sidebar to un-focus, so they update properly
 
     outputDevicesChanged = pyqtSignal()
-    currentConfigurationChanged = pyqtSignal() # Emitted every time the current configurations of the machine changes
+    currentConfigurationChanged = pyqtSignal()  # Emitted every time the current configurations of the machine changes
     printerConnectedStatusChanged = pyqtSignal() # Emitted every time the active machine change or the outputdevices change
 
     rootMaterialChanged = pyqtSignal()
@@ -203,7 +203,7 @@ class MachineManager(QObject):
             extruder_configuration.hotendID = extruder.variant.getName() if extruder.variant != empty_variant_container else None
             self._current_printer_configuration.extruderConfigurations.append(extruder_configuration)
 
-        # an empty build plate configuration from the network printer is presented as an empty string, so use "" for an
+        # An empty build plate configuration from the network printer is presented as an empty string, so use "" for an
         # empty build plate.
         self._current_printer_configuration.buildplateConfiguration = self._global_container_stack.getProperty("machine_buildplate_type", "value") if self._global_container_stack.variant != empty_variant_container else ""
         self.currentConfigurationChanged.emit()
@@ -249,7 +249,7 @@ class MachineManager(QObject):
             self.updateNumberExtrudersEnabled()
         self.globalContainerChanged.emit()
 
-        # after switching the global stack we reconnect all the signals and set the variant and material references
+        # After switching the global stack we reconnect all the signals and set the variant and material references
         if self._global_container_stack:
             self._application.getPreferences().setValue("cura/active_machine", self._global_container_stack.getId())
 
@@ -263,7 +263,7 @@ class MachineManager(QObject):
                 if global_variant.getMetaDataEntry("hardware_type") != "buildplate":
                     self._global_container_stack.setVariant(empty_variant_container)
 
-            # set the global material to empty as we now use the extruder stack at all times - CURA-4482
+            # 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
             if global_material != empty_material_container:
                 self._global_container_stack.setMaterial(empty_material_container)
@@ -421,7 +421,7 @@ class MachineManager(QObject):
         # Not a very pretty solution, but the extruder manager doesn't really know how many extruders there are
         machine_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
         extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
-        count = 1  # we start with the global stack
+        count = 1  # We start with the global stack
         for stack in extruder_stacks:
             md = stack.getMetaData()
             if "position" in md and int(md["position"]) >= machine_extruder_count:
@@ -618,6 +618,14 @@ class MachineManager(QObject):
                 is_supported = self._current_quality_group.is_available
         return is_supported
 
+    @pyqtProperty(bool, notify = activeQualityGroupChanged)
+    def isActiveQualityExperimental(self) -> bool:
+        is_experimental = False
+        if self._global_container_stack:
+            if self._current_quality_group:
+                is_experimental = self._current_quality_group.is_experimental
+        return is_experimental
+
     ##  Returns whether there is anything unsupported in the current set-up.
     #
     #   The current set-up signifies the global stack and all extruder stacks,
@@ -648,7 +656,7 @@ class MachineManager(QObject):
         new_value = self._active_container_stack.getProperty(key, "value")
         extruder_stacks = [stack for stack in ExtruderManager.getInstance().getActiveExtruderStacks()]
 
-        # check in which stack the value has to be replaced
+        # Check in which stack the value has to be replaced
         for extruder_stack in extruder_stacks:
             if extruder_stack != self._active_container_stack and extruder_stack.getProperty(key, "value") != new_value:
                 extruder_stack.userChanges.setProperty(key, "value", new_value)  # TODO: nested property access, should be improved
@@ -664,7 +672,7 @@ class MachineManager(QObject):
                 for key in self._active_container_stack.userChanges.getAllKeys():
                     new_value = self._active_container_stack.getProperty(key, "value")
 
-                    # check if the value has to be replaced
+                    # Check if the value has to be replaced
                     extruder_stack.userChanges.setProperty(key, "value", new_value)
 
     @pyqtProperty(str, notify = activeVariantChanged)
@@ -733,7 +741,7 @@ class MachineManager(QObject):
         # 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)
 
-        # activate a new machine before removing a machine because this is safer
+        # Activate a new machine before removing a machine because this is safer
         if activate_new_machine:
             machine_stacks = CuraContainerRegistry.getInstance().findContainerStacksMetadata(type = "machine")
             other_machine_stacks = [s for s in machine_stacks if s["id"] != machine_id]
@@ -917,9 +925,12 @@ class MachineManager(QObject):
 
             if settable_per_extruder:
                 limit_to_extruder = int(self._global_container_stack.getProperty(setting_key, "limit_to_extruder"))
-                extruder_position = str(max(0, limit_to_extruder))
-                extruder_stack = self._global_container_stack.extruders[extruder_position]
-                extruder_stack.userChanges.setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value"))
+                extruder_position = max(0, limit_to_extruder)
+                extruder_stack = self.getExtruder(extruder_position)
+                if extruder_stack:
+                    extruder_stack.userChanges.setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value"))
+                else:
+                    Logger.log("e", "Unable to find extruder on position %s", extruder_position)
                 global_user_container.removeInstance(setting_key)
 
         # Signal that the global stack has changed
@@ -928,10 +939,9 @@ class MachineManager(QObject):
 
     @pyqtSlot(int, result = QObject)
     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
+            return self._global_container_stack.extruders.get(str(position))
+        return None
 
     def updateDefaultExtruder(self) -> None:
         if self._global_container_stack is None:
@@ -993,12 +1003,16 @@ class MachineManager(QObject):
         self.updateNumberExtrudersEnabled()
         self.correctExtruderSettings()
 
-        # ensure that the quality profile is compatible with current combination, or choose a compatible one if available
+        # In case this extruder is being disabled and it's the currently selected one, switch to the default extruder
+        if not enabled and position == ExtruderManager.getInstance().activeExtruderIndex:
+            ExtruderManager.getInstance().setActiveExtruderIndex(int(self._default_extruder_position))
+
+        # Ensure that the quality profile is compatible with current combination, or choose a compatible one if available
         self._updateQualityWithMaterial()
         self.extruderChanged.emit()
-        # update material compatibility color
+        # Update material compatibility color
         self.activeQualityGroupChanged.emit()
-        # update items in SettingExtruder
+        # Update items in SettingExtruder
         ExtruderManager.getInstance().extrudersChanged.emit(self._global_container_stack.getId())
         # Make sure the front end reflects changes
         self.forceUpdateAllSettings()
@@ -1072,7 +1086,6 @@ class MachineManager(QObject):
 
         return result
 
-    #
     # Sets all quality and quality_changes containers to empty_quality and empty_quality_changes containers
     # for all stacks in the currently active machine.
     #
@@ -1131,7 +1144,7 @@ class MachineManager(QObject):
 
     def _setQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup") -> None:
         if self._global_container_stack is None:
-            return #Can't change that.
+            return  # Can't change that.
         quality_type = quality_changes_group.quality_type
         # A custom quality can be created based on "not supported".
         # In that case, do not set quality containers to empty.
@@ -1201,7 +1214,7 @@ class MachineManager(QObject):
             self.rootMaterialChanged.emit()
 
     def activeMaterialsCompatible(self) -> bool:
-        # check material - variant compatibility
+        # Check material - variant compatibility
         if self._global_container_stack is not None:
             if Util.parseBool(self._global_container_stack.getMetaDataEntry("has_materials", False)):
                 for position, extruder in self._global_container_stack.extruders.items():
@@ -1411,7 +1424,7 @@ class MachineManager(QObject):
                                                                material_diameter, root_material_id)
         self.setMaterial(position, material_node)
 
-    ##  global_stack: if you want to provide your own global_stack instead of the current active one
+    ##  Global_stack: if you want to provide your own global_stack instead of the current active one
     #   if you update an active machine, special measures have to be taken.
     @pyqtSlot(str, "QVariant")
     def setMaterial(self, position: str, container_node, global_stack: Optional["GlobalStack"] = None) -> None:

+ 1 - 1
plugins/CuraEngineBackend/StartSliceJob.py

@@ -72,7 +72,7 @@ class GcodeStartEndFormatter(Formatter):
         # "-1" is global stack, and if the setting value exists in the global stack, use it as the fallback value.
         if key in kwargs["-1"]:
             value = kwargs["-1"]
-        if key in kwargs[str(extruder_nr)]:
+        if str(extruder_nr) in kwargs and key in kwargs[str(extruder_nr)]:
             value = kwargs[str(extruder_nr)][key]
 
         if value == default_value_str:

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