Browse Source

Merge pull request #3485 from Ultimaker/CURA-5059_reset_icon_visibility

Cura 5059 reset icon visibility
Lipu Fei 7 years ago
parent
commit
e91b955f1e

+ 19 - 6
cura/CuraApplication.py

@@ -67,6 +67,8 @@ from cura.Machines.Models.MaterialManagementModel import MaterialManagementModel
 from cura.Machines.Models.GenericMaterialsModel import GenericMaterialsModel
 from cura.Machines.Models.GenericMaterialsModel import GenericMaterialsModel
 from cura.Machines.Models.BrandMaterialsModel import BrandMaterialsModel
 from cura.Machines.Models.BrandMaterialsModel import BrandMaterialsModel
 
 
+from cura.Machines.MachineErrorChecker import MachineErrorChecker
+
 from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
 from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
 from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
 from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
 
 
@@ -142,12 +144,6 @@ class CuraApplication(QtApplication):
 
 
     Q_ENUMS(ResourceTypes)
     Q_ENUMS(ResourceTypes)
 
 
-    # FIXME: This signal belongs to the MachineManager, but the CuraEngineBackend plugin requires on it.
-    #        Because plugins are initialized before the ContainerRegistry, putting this signal in MachineManager
-    #        will make it initialized before ContainerRegistry does, and it won't find the active machine, thus
-    #        Cura will always show the Add Machine Dialog upon start.
-    stacksValidationFinished = pyqtSignal()  # Emitted whenever a validation is finished
-
     def __init__(self, **kwargs):
     def __init__(self, **kwargs):
         # this list of dir names will be used by UM to detect an old cura directory
         # this list of dir names will be used by UM to detect an old cura directory
         for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "user", "variants"]:
         for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "user", "variants"]:
@@ -224,12 +220,14 @@ class CuraApplication(QtApplication):
         self._machine_manager = None    # This is initialized on demand.
         self._machine_manager = None    # This is initialized on demand.
         self._extruder_manager = None
         self._extruder_manager = None
         self._material_manager = None
         self._material_manager = None
+        self._quality_manager = None
         self._object_manager = None
         self._object_manager = None
         self._build_plate_model = None
         self._build_plate_model = None
         self._multi_build_plate_model = None
         self._multi_build_plate_model = None
         self._setting_inheritance_manager = None
         self._setting_inheritance_manager = None
         self._simple_mode_settings_manager = None
         self._simple_mode_settings_manager = None
         self._cura_scene_controller = None
         self._cura_scene_controller = None
+        self._machine_error_checker = None
 
 
         self._additional_components = {} # Components to add to certain areas in the interface
         self._additional_components = {} # Components to add to certain areas in the interface
 
 
@@ -743,19 +741,28 @@ class CuraApplication(QtApplication):
         self.preRun()
         self.preRun()
 
 
         container_registry = ContainerRegistry.getInstance()
         container_registry = ContainerRegistry.getInstance()
+
+        Logger.log("i", "Initializing variant manager")
         self._variant_manager = VariantManager(container_registry)
         self._variant_manager = VariantManager(container_registry)
         self._variant_manager.initialize()
         self._variant_manager.initialize()
 
 
+        Logger.log("i", "Initializing material manager")
         from cura.Machines.MaterialManager import MaterialManager
         from cura.Machines.MaterialManager import MaterialManager
         self._material_manager = MaterialManager(container_registry, parent = self)
         self._material_manager = MaterialManager(container_registry, parent = self)
         self._material_manager.initialize()
         self._material_manager.initialize()
 
 
+        Logger.log("i", "Initializing quality manager")
         from cura.Machines.QualityManager import QualityManager
         from cura.Machines.QualityManager import QualityManager
         self._quality_manager = QualityManager(container_registry, parent = self)
         self._quality_manager = QualityManager(container_registry, parent = self)
         self._quality_manager.initialize()
         self._quality_manager.initialize()
 
 
+        Logger.log("i", "Initializing machine manager")
         self._machine_manager = MachineManager(self)
         self._machine_manager = MachineManager(self)
 
 
+        Logger.log("i", "Initializing machine error checker")
+        self._machine_error_checker = MachineErrorChecker(self)
+        self._machine_error_checker.initialize()
+
         # Check if we should run as single instance or not
         # Check if we should run as single instance or not
         self._setUpSingleInstanceServer()
         self._setUpSingleInstanceServer()
 
 
@@ -781,8 +788,11 @@ class CuraApplication(QtApplication):
             self._openFile(file_name)
             self._openFile(file_name)
 
 
         self.started = True
         self.started = True
+        self.initializationFinished.emit()
         self.exec_()
         self.exec_()
 
 
+    initializationFinished = pyqtSignal()
+
     ##  Run Cura without GUI elements and interaction (server mode).
     ##  Run Cura without GUI elements and interaction (server mode).
     def runWithoutGUI(self):
     def runWithoutGUI(self):
         self._use_gui = False
         self._use_gui = False
@@ -847,6 +857,9 @@ class CuraApplication(QtApplication):
     def hasGui(self):
     def hasGui(self):
         return self._use_gui
         return self._use_gui
 
 
+    def getMachineErrorChecker(self, *args) -> MachineErrorChecker:
+        return self._machine_error_checker
+
     def getMachineManager(self, *args) -> MachineManager:
     def getMachineManager(self, *args) -> MachineManager:
         if self._machine_manager is None:
         if self._machine_manager is None:
             self._machine_manager = MachineManager(self)
             self._machine_manager = MachineManager(self)

+ 181 - 0
cura/Machines/MachineErrorChecker.py

@@ -0,0 +1,181 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+import time
+
+from collections import deque
+
+from PyQt5.QtCore import QObject, QTimer, pyqtSignal, pyqtProperty
+
+from UM.Application import Application
+from UM.Logger import Logger
+from UM.Settings.SettingDefinition import SettingDefinition
+from UM.Settings.Validator import ValidatorState
+
+
+#
+# This class performs setting error checks for the currently active machine.
+#
+# The whole error checking process is pretty heavy which can take ~0.5 secs, so it can cause GUI to lag.
+# The idea here is to split the whole error check into small tasks, each of which only checks a single setting key
+# in a stack. According to my profiling results, the maximal runtime for such a sub-task is <0.03 secs, which should
+# be good enough. Moreover, if any changes happened to the machine, we can cancel the check in progress without wait
+# for it to finish the complete work.
+#
+class MachineErrorChecker(QObject):
+
+    def __init__(self, parent = None):
+        super().__init__(parent)
+
+        self._global_stack = None
+
+        self._has_errors = True  # Result of the error check, indicating whether there are errors in the stack
+        self._error_keys = set()  # A set of settings keys that have errors
+        self._error_keys_in_progress = set()  # The variable that stores the results of the currently in progress check
+
+        self._stacks_and_keys_to_check = None  # a FIFO queue of tuples (stack, key) to check for errors
+
+        self._need_to_check = False  # Whether we need to schedule a new check or not. This flag is set when a new
+                                     # error check needs to take place while there is already one running at the moment.
+        self._check_in_progress = False  # Whether there is an error check running in progress at the moment.
+
+        self._application = Application.getInstance()
+        self._machine_manager = self._application.getMachineManager()
+
+        self._start_time = 0  # measure checking time
+
+        # This timer delays the starting of error check so we can react less frequently if the user is frequently
+        # changing settings.
+        self._error_check_timer = QTimer(self)
+        self._error_check_timer.setInterval(100)
+        self._error_check_timer.setSingleShot(True)
+
+    def initialize(self):
+        self._error_check_timer.timeout.connect(self._rescheduleCheck)
+
+        # Reconnect all signals when the active machine gets changed.
+        self._machine_manager.globalContainerChanged.connect(self._onMachineChanged)
+
+        # Whenever the machine settings get changed, we schedule an error check.
+        self._machine_manager.globalContainerChanged.connect(self.startErrorCheck)
+        self._machine_manager.globalValueChanged.connect(self.startErrorCheck)
+
+        self._onMachineChanged()
+
+    def _onMachineChanged(self):
+        if self._global_stack:
+            self._global_stack.propertyChanged.disconnect(self.startErrorCheck)
+            self._global_stack.containersChanged.disconnect(self.startErrorCheck)
+
+            for extruder in self._global_stack.extruders.values():
+                extruder.propertyChanged.disconnect(self.startErrorCheck)
+                extruder.containersChanged.disconnect(self.startErrorCheck)
+
+        self._global_stack = self._machine_manager.activeMachine
+
+        if self._global_stack:
+            self._global_stack.propertyChanged.connect(self.startErrorCheck)
+            self._global_stack.containersChanged.connect(self.startErrorCheck)
+
+            for extruder in self._global_stack.extruders.values():
+                extruder.propertyChanged.connect(self.startErrorCheck)
+                extruder.containersChanged.connect(self.startErrorCheck)
+
+    hasErrorUpdated = pyqtSignal()
+    needToWaitForResultChanged = pyqtSignal()
+    errorCheckFinished = pyqtSignal()
+
+    @pyqtProperty(bool, notify = hasErrorUpdated)
+    def hasError(self) -> bool:
+        return self._has_errors
+
+    @pyqtProperty(bool, notify = needToWaitForResultChanged)
+    def needToWaitForResult(self) -> bool:
+        return self._need_to_check or self._check_in_progress
+
+    # Starts the error check timer to schedule a new error check.
+    def startErrorCheck(self, *args):
+        if not self._check_in_progress:
+            self._need_to_check = True
+            self.needToWaitForResultChanged.emit()
+        self._error_check_timer.start()
+
+    # This function is called by the timer to reschedule a new error check.
+    # If there is no check in progress, it will start a new one. If there is any, it sets the "_need_to_check" flag
+    # to notify the current check to stop and start a new one.
+    def _rescheduleCheck(self):
+        if self._check_in_progress and not self._need_to_check:
+            self._need_to_check = True
+            self.needToWaitForResultChanged.emit()
+            return
+
+        self._error_keys_in_progress = set()
+        self._need_to_check = False
+        self.needToWaitForResultChanged.emit()
+
+        global_stack = self._machine_manager.activeMachine
+        if global_stack is None:
+            Logger.log("i", "No active machine, nothing to check.")
+            return
+
+        # Populate the (stack, key) tuples to check
+        self._stacks_and_keys_to_check = deque()
+        for stack in [global_stack] + list(global_stack.extruders.values()):
+            for key in stack.getAllKeys():
+                self._stacks_and_keys_to_check.append((stack, key))
+
+        self._application.callLater(self._checkStack)
+        self._start_time = time.time()
+        Logger.log("d", "New error check scheduled.")
+
+    def _checkStack(self):
+        if self._need_to_check:
+            Logger.log("d", "Need to check for errors again. Discard the current progress and reschedule a check.")
+            self._check_in_progress = False
+            self._application.callLater(self.startErrorCheck)
+            return
+
+        self._check_in_progress = True
+
+        # If there is nothing to check any more, it means there is no error.
+        if not self._stacks_and_keys_to_check:
+            # Finish
+            self._setResult(False)
+            return
+
+        # Get the next stack and key to check
+        stack, key = self._stacks_and_keys_to_check.popleft()
+
+        enabled = stack.getProperty(key, "enabled")
+        if not enabled:
+            self._application.callLater(self._checkStack)
+            return
+
+        validation_state = stack.getProperty(key, "validationState")
+        if validation_state is None:
+            # Setting is not validated. This can happen if there is only a setting definition.
+            # We do need to validate it, because a setting definitions value can be set by a function, which could
+            # be an invalid setting.
+            definition = stack.getSettingDefinition(key)
+            validator_type = SettingDefinition.getValidatorForType(definition.type)
+            if validator_type:
+                validator = validator_type(key)
+                validation_state = validator(stack)
+        if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
+            # Finish
+            self._setResult(True)
+            return
+
+        # Schedule the check for the next key
+        self._application.callLater(self._checkStack)
+
+    def _setResult(self, result: bool):
+        if result != self._has_errors:
+            self._has_errors = result
+            self.hasErrorUpdated.emit()
+            self._machine_manager.stacksValidationChanged.emit()
+        self._need_to_check = False
+        self._check_in_progress = False
+        self.needToWaitForResultChanged.emit()
+        self.errorCheckFinished.emit()
+        Logger.log("i", "Error check finished, result = %s, time = %0.1fs", result, time.time() - self._start_time)

+ 5 - 24
cura/Settings/MachineManager.py

@@ -4,7 +4,7 @@
 import collections
 import collections
 import time
 import time
 #Type hinting.
 #Type hinting.
-from typing import Union, List, Dict, TYPE_CHECKING, Optional
+from typing import List, Dict, TYPE_CHECKING, Optional
 
 
 from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
 from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
 from UM.Signal import Signal
 from UM.Signal import Signal
@@ -20,7 +20,6 @@ from UM.Logger import Logger
 from UM.Message import Message
 from UM.Message import Message
 
 
 from UM.Settings.ContainerRegistry import ContainerRegistry
 from UM.Settings.ContainerRegistry import ContainerRegistry
-from UM.Settings.InstanceContainer import InstanceContainer
 from UM.Settings.SettingFunction import SettingFunction
 from UM.Settings.SettingFunction import SettingFunction
 from UM.Signal import postponeSignals, CompressTechnique
 from UM.Signal import postponeSignals, CompressTechnique
 
 
@@ -56,11 +55,6 @@ class MachineManager(QObject):
 
 
         self.machine_extruder_material_update_dict = collections.defaultdict(list)
         self.machine_extruder_material_update_dict = collections.defaultdict(list)
 
 
-        self._error_check_timer = QTimer()
-        self._error_check_timer.setInterval(250)
-        self._error_check_timer.setSingleShot(True)
-        self._error_check_timer.timeout.connect(self._updateStacksHaveErrors)
-
         self._instance_container_timer = QTimer()
         self._instance_container_timer = QTimer()
         self._instance_container_timer.setInterval(250)
         self._instance_container_timer.setInterval(250)
         self._instance_container_timer.setSingleShot(True)
         self._instance_container_timer.setSingleShot(True)
@@ -228,15 +222,6 @@ class MachineManager(QObject):
                 del self.machine_extruder_material_update_dict[self._global_container_stack.getId()]
                 del self.machine_extruder_material_update_dict[self._global_container_stack.getId()]
 
 
         self.activeQualityGroupChanged.emit()
         self.activeQualityGroupChanged.emit()
-        self._error_check_timer.start()
-
-    ##  Update self._stacks_valid according to _checkStacksForErrors and emit if change.
-    def _updateStacksHaveErrors(self) -> None:
-        old_stacks_have_errors = self._stacks_have_errors
-        self._stacks_have_errors = self._checkStacksHaveErrors()
-        if old_stacks_have_errors != self._stacks_have_errors:
-            self.stacksValidationChanged.emit()
-        Application.getInstance().stacksValidationFinished.emit()
 
 
     def _onActiveExtruderStackChanged(self) -> None:
     def _onActiveExtruderStackChanged(self) -> None:
         self.blurSettings.emit()  # Ensure no-one has focus.
         self.blurSettings.emit()  # Ensure no-one has focus.
@@ -256,8 +241,6 @@ class MachineManager(QObject):
 
 
         self.rootMaterialChanged.emit()
         self.rootMaterialChanged.emit()
 
 
-        self._error_check_timer.start()
-
     def _onInstanceContainersChanged(self, container) -> None:
     def _onInstanceContainersChanged(self, container) -> None:
         self._instance_container_timer.start()
         self._instance_container_timer.start()
 
 
@@ -266,9 +249,6 @@ class MachineManager(QObject):
             # Notify UI items, such as the "changed" star in profile pull down menu.
             # Notify UI items, such as the "changed" star in profile pull down menu.
             self.activeStackValueChanged.emit()
             self.activeStackValueChanged.emit()
 
 
-        elif property_name == "validationState":
-            self._error_check_timer.start()
-
     ## Given a global_stack, make sure that it's all valid by searching for this quality group and applying it again
     ## 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):
         material_dict = {}
         material_dict = {}
@@ -832,9 +812,10 @@ class MachineManager(QObject):
 
 
     ##  This will fire the propertiesChanged for all settings so they will be updated in the front-end
     ##  This will fire the propertiesChanged for all settings so they will be updated in the front-end
     def forceUpdateAllSettings(self):
     def forceUpdateAllSettings(self):
-        property_names = ["value", "resolve"]
-        for setting_key in self._global_container_stack.getAllKeys():
-            self._global_container_stack.propertiesChanged.emit(setting_key, property_names)
+        with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
+            property_names = ["value", "resolve"]
+            for setting_key in self._global_container_stack.getAllKeys():
+                self._global_container_stack.propertiesChanged.emit(setting_key, property_names)
 
 
     @pyqtSlot(int, bool)
     @pyqtSlot(int, bool)
     def setExtruderEnabled(self, position: int, enabled) -> None:
     def setExtruderEnabled(self, position: int, enabled) -> None:

+ 45 - 30
plugins/CuraEngineBackend/CuraEngineBackend.py

@@ -10,7 +10,6 @@ from UM.Logger import Logger
 from UM.Message import Message
 from UM.Message import Message
 from UM.PluginRegistry import PluginRegistry
 from UM.PluginRegistry import PluginRegistry
 from UM.Resources import Resources
 from UM.Resources import Resources
-from UM.Settings.Validator import ValidatorState #To find if a setting is in an error state. We can't slice then.
 from UM.Platform import Platform
 from UM.Platform import Platform
 from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
 from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
 from UM.Qt.Duration import DurationFormat
 from UM.Qt.Duration import DurationFormat
@@ -32,6 +31,7 @@ import Arcus
 from UM.i18n import i18nCatalog
 from UM.i18n import i18nCatalog
 catalog = i18nCatalog("cura")
 catalog = i18nCatalog("cura")
 
 
+
 class CuraEngineBackend(QObject, Backend):
 class CuraEngineBackend(QObject, Backend):
 
 
     backendError = Signal()
     backendError = Signal()
@@ -62,23 +62,26 @@ class CuraEngineBackend(QObject, Backend):
                     default_engine_location = execpath
                     default_engine_location = execpath
                     break
                     break
 
 
+        self._application = Application.getInstance()
+        self._multi_build_plate_model = None
+        self._machine_error_checker = None
+
         if not default_engine_location:
         if not default_engine_location:
             raise EnvironmentError("Could not find CuraEngine")
             raise EnvironmentError("Could not find CuraEngine")
 
 
-        Logger.log("i", "Found CuraEngine at: %s" %(default_engine_location))
+        Logger.log("i", "Found CuraEngine at: %s", default_engine_location)
 
 
         default_engine_location = os.path.abspath(default_engine_location)
         default_engine_location = os.path.abspath(default_engine_location)
         Preferences.getInstance().addPreference("backend/location", default_engine_location)
         Preferences.getInstance().addPreference("backend/location", default_engine_location)
 
 
         # Workaround to disable layer view processing if layer view is not active.
         # Workaround to disable layer view processing if layer view is not active.
         self._layer_view_active = False
         self._layer_view_active = False
-        Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
-        Application.getInstance().getMultiBuildPlateModel().activeBuildPlateChanged.connect(self._onActiveViewChanged)
         self._onActiveViewChanged()
         self._onActiveViewChanged()
+
         self._stored_layer_data = []
         self._stored_layer_data = []
         self._stored_optimized_layer_data = {}  # key is build plate number, then arrays are stored until they go to the ProcessSlicesLayersJob
         self._stored_optimized_layer_data = {}  # key is build plate number, then arrays are stored until they go to the ProcessSlicesLayersJob
 
 
-        self._scene = Application.getInstance().getController().getScene()
+        self._scene = self._application.getController().getScene()
         self._scene.sceneChanged.connect(self._onSceneChanged)
         self._scene.sceneChanged.connect(self._onSceneChanged)
 
 
         # Triggers for auto-slicing. Auto-slicing is triggered as follows:
         # Triggers for auto-slicing. Auto-slicing is triggered as follows:
@@ -86,20 +89,10 @@ class CuraEngineBackend(QObject, Backend):
         #  - whenever there is a value change, we start the timer
         #  - whenever there is a value change, we start the timer
         #  - sometimes an error check can get scheduled for a value change, in that case, we ONLY want to start the
         #  - sometimes an error check can get scheduled for a value change, in that case, we ONLY want to start the
         #    auto-slicing timer when that error check is finished
         #    auto-slicing timer when that error check is finished
-        #  If there is an error check, it will set the "_is_error_check_scheduled" flag, stop the auto-slicing timer,
-        #  and only wait for the error check to be finished to start the auto-slicing timer again.
+        # If there is an error check, stop the auto-slicing timer, and only wait for the error check to be finished
+        # to start the auto-slicing timer again.
         #
         #
         self._global_container_stack = None
         self._global_container_stack = None
-        Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
-        self._onGlobalStackChanged()
-
-        Application.getInstance().stacksValidationFinished.connect(self._onStackErrorCheckFinished)
-        # extruder enable / disable. Actually wanted to use machine manager here, but the initialization order causes it to crash
-        ExtruderManager.getInstance().extrudersChanged.connect(self._extruderChanged)
-
-        # A flag indicating if an error check was scheduled
-        # If so, we will stop the auto-slice timer and start upon the error check
-        self._is_error_check_scheduled = False
 
 
         # Listeners for receiving messages from the back-end.
         # Listeners for receiving messages from the back-end.
         self._message_handlers["cura.proto.Layer"] = self._onLayerMessage
         self._message_handlers["cura.proto.Layer"] = self._onLayerMessage
@@ -125,13 +118,6 @@ class CuraEngineBackend(QObject, Backend):
         self._last_num_objects = defaultdict(int)  # Count number of objects to see if there is something changed
         self._last_num_objects = defaultdict(int)  # Count number of objects to see if there is something changed
         self._postponed_scene_change_sources = []  # scene change is postponed (by a tool)
         self._postponed_scene_change_sources = []  # scene change is postponed (by a tool)
 
 
-        self.backendQuit.connect(self._onBackendQuit)
-        self.backendConnected.connect(self._onBackendConnected)
-
-        # When a tool operation is in progress, don't slice. So we need to listen for tool operations.
-        Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted)
-        Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped)
-
         self._slice_start_time = None
         self._slice_start_time = None
 
 
         Preferences.getInstance().addPreference("general/auto_slice", True)
         Preferences.getInstance().addPreference("general/auto_slice", True)
@@ -146,6 +132,30 @@ class CuraEngineBackend(QObject, Backend):
         self.determineAutoSlicing()
         self.determineAutoSlicing()
         Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
         Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
 
 
+        self._application.initializationFinished.connect(self.initialize)
+
+    def initialize(self):
+        self._multi_build_plate_model = self._application.getMultiBuildPlateModel()
+
+        self._application.getController().activeViewChanged.connect(self._onActiveViewChanged)
+        self._multi_build_plate_model.activeBuildPlateChanged.connect(self._onActiveViewChanged)
+
+        self._application.globalContainerStackChanged.connect(self._onGlobalStackChanged)
+        self._onGlobalStackChanged()
+
+        # extruder enable / disable. Actually wanted to use machine manager here, but the initialization order causes it to crash
+        ExtruderManager.getInstance().extrudersChanged.connect(self._extruderChanged)
+
+        self.backendQuit.connect(self._onBackendQuit)
+        self.backendConnected.connect(self._onBackendConnected)
+
+        # When a tool operation is in progress, don't slice. So we need to listen for tool operations.
+        self._application.getController().toolOperationStarted.connect(self._onToolOperationStarted)
+        self._application.getController().toolOperationStopped.connect(self._onToolOperationStopped)
+
+        self._machine_error_checker = self._application.getMachineErrorChecker()
+        self._machine_error_checker.errorCheckFinished.connect(self._onStackErrorCheckFinished)
+
     ##  Terminate the engine process.
     ##  Terminate the engine process.
     #
     #
     #   This function should terminate the engine process.
     #   This function should terminate the engine process.
@@ -531,11 +541,9 @@ class CuraEngineBackend(QObject, Backend):
 
 
         elif property == "validationState":
         elif property == "validationState":
             if self._use_timer:
             if self._use_timer:
-                self._is_error_check_scheduled = True
                 self._change_timer.stop()
                 self._change_timer.stop()
 
 
     def _onStackErrorCheckFinished(self):
     def _onStackErrorCheckFinished(self):
-        self._is_error_check_scheduled = False
         if not self._slicing and self._build_plates_to_be_sliced:
         if not self._slicing and self._build_plates_to_be_sliced:
             self.needsSlicing()
             self.needsSlicing()
             self._onChanged()
             self._onChanged()
@@ -561,12 +569,15 @@ class CuraEngineBackend(QObject, Backend):
         self.processingProgress.emit(message.amount)
         self.processingProgress.emit(message.amount)
         self.backendStateChange.emit(BackendState.Processing)
         self.backendStateChange.emit(BackendState.Processing)
 
 
-    # testing
     def _invokeSlice(self):
     def _invokeSlice(self):
         if self._use_timer:
         if self._use_timer:
             # if the error check is scheduled, wait for the error check finish signal to trigger auto-slice,
             # if the error check is scheduled, wait for the error check finish signal to trigger auto-slice,
             # otherwise business as usual
             # otherwise business as usual
-            if self._is_error_check_scheduled:
+            if self._machine_error_checker is None:
+                self._change_timer.stop()
+                return
+
+            if self._machine_error_checker.needToWaitForResult:
                 self._change_timer.stop()
                 self._change_timer.stop()
             else:
             else:
                 self._change_timer.start()
                 self._change_timer.start()
@@ -632,7 +643,11 @@ class CuraEngineBackend(QObject, Backend):
         if self._use_timer:
         if self._use_timer:
             # if the error check is scheduled, wait for the error check finish signal to trigger auto-slice,
             # if the error check is scheduled, wait for the error check finish signal to trigger auto-slice,
             # otherwise business as usual
             # otherwise business as usual
-            if self._is_error_check_scheduled:
+            if self._machine_error_checker is None:
+                self._change_timer.stop()
+                return
+
+            if self._machine_error_checker.needToWaitForResult:
                 self._change_timer.stop()
                 self._change_timer.stop()
             else:
             else:
                 self._change_timer.start()
                 self._change_timer.start()
@@ -786,7 +801,7 @@ class CuraEngineBackend(QObject, Backend):
             self._change_timer.start()
             self._change_timer.start()
 
 
     def _extruderChanged(self):
     def _extruderChanged(self):
-        for build_plate_number in range(Application.getInstance().getMultiBuildPlateModel().maxBuildPlate + 1):
+        for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1):
             if build_plate_number not in self._build_plates_to_be_sliced:
             if build_plate_number not in self._build_plates_to_be_sliced:
                 self._build_plates_to_be_sliced.append(build_plate_number)
                 self._build_plates_to_be_sliced.append(build_plate_number)
         self._invokeSlice()
         self._invokeSlice()

+ 0 - 2
resources/qml/Settings/SettingView.qml

@@ -374,8 +374,6 @@ Item
                     key: model.key ? model.key : ""
                     key: model.key ? model.key : ""
                     watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder", "resolve" ]
                     watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder", "resolve" ]
                     storeIndex: 0
                     storeIndex: 0
-                    // Due to the way setPropertyValue works, removeUnusedValue gives the correct output in case of resolve
-                    removeUnusedValue: model.resolve == undefined
                 }
                 }
 
 
                 Connections
                 Connections