diff --git a/cura/ObjectsModel.py b/cura/ObjectsModel.py index f02e8b4db5..d7077d3d85 100644 --- a/cura/ObjectsModel.py +++ b/cura/ObjectsModel.py @@ -19,6 +19,8 @@ class ObjectsModel(ListModel): self._build_plate_number = -1 + self._stacks_have_errors = None # type:Optional[bool] + def setActiveBuildPlate(self, nr): self._build_plate_number = nr self._update() @@ -67,3 +69,11 @@ class ObjectsModel(ListModel): @staticmethod def createObjectsModel(): return ObjectsModel() + + ## Check if none of the model's stacks contain error states + # The setting applied for the settings per model + def stacksHaveErrors(self) -> bool: + return bool(self._stacks_have_errors) + + def setStacksHaveErrors(self, value): + self._stacks_have_errors = value \ No newline at end of file diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index b19c387a14..8b7205f8b2 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -136,6 +136,11 @@ class StartSliceJob(Job): self.setResult(StartJobResult.MaterialIncompatible) return + # Validate settings per selectable model + if Application.getInstance().getObjectsModel().stacksHaveErrors(): + self.setResult(StartJobResult.ObjectSettingError) + return + # Don't slice if there is a per object setting with an error value. for node in DepthFirstIterator(self._scene.getRoot()): if node.isSelectable(): diff --git a/plugins/PerObjectSettingsTool/PerObjectItem.qml b/plugins/PerObjectSettingsTool/PerObjectItem.qml index 559ad2bf81..89679b57a2 100644 --- a/plugins/PerObjectSettingsTool/PerObjectItem.qml +++ b/plugins/PerObjectSettingsTool/PerObjectItem.qml @@ -25,7 +25,20 @@ UM.TooltipArea onClicked: { - addedSettingsModel.setVisible(model.key, checked); + // Important first set visible and then subscribe + // otherwise the setting is not yet in list + // For unsubscribe is important first remove the subscription and then + // set as invisible + if(checked) + { + addedSettingsModel.setVisible(model.key, checked); + UM.ActiveTool.triggerAction("subscribeForSettingValidation", model.key) + } + else + { + UM.ActiveTool.triggerAction("unsubscribeForSettingValidation", model.key) + addedSettingsModel.setVisible(model.key, checked); + } UM.ActiveTool.forceUpdate(); } } diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index 03a2ce1bf4..c591bc9ae3 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -54,6 +54,12 @@ Item { id: meshTypeSelection style: UM.Theme.styles.combobox onActivated: { + + console.log("!!!!!!!!!!!!!!!!!!!") + + if(model.get(index).type == "anti_overhang_mesh") + console.log("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") + UM.ActiveTool.setProperty("MeshType", model.get(index).type) } model: ListModel @@ -240,7 +246,10 @@ Item { width: Math.round(UM.Theme.getSize("setting").height / 2) height: UM.Theme.getSize("setting").height - onClicked: addedSettingsModel.setVisible(model.key, false) + onClicked: { + UM.ActiveTool.triggerAction("unsubscribeForSettingValidation", model.key) + addedSettingsModel.setVisible(model.key, false) + } style: ButtonStyle { diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py index d2db5ff420..aa8bb92045 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py @@ -10,7 +10,10 @@ from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator from cura.Settings.ExtruderManager import ExtruderManager from UM.Settings.SettingInstance import SettingInstance from UM.Event import Event +from UM.Settings.Validator import ValidatorState +from UM.Logger import Logger +from PyQt5.QtCore import QTimer ## This tool allows the user to add & change settings per node in the scene. # The settings per object are kept in a ContainerStack, which is linked to a node by decorator. @@ -34,6 +37,13 @@ class PerObjectSettingsTool(Tool): self._onGlobalContainerChanged() Selection.selectionChanged.connect(self._updateEnabled) + self._scene = Application.getInstance().getController().getScene() + + 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) + def event(self, event): super().event(event) @@ -142,3 +152,62 @@ class PerObjectSettingsTool(Tool): else: self._single_model_selected = True Application.getInstance().getController().toolEnabledChanged.emit(self._plugin_id, self._advanced_mode and self._single_model_selected) + + + def _onPropertyChanged(self, key: str, property_name: str) -> None: + if property_name == "validationState": + self._error_check_timer.start() + + def _updateStacksHaveErrors(self) -> None: + self._checkStacksHaveErrors() + + + def _checkStacksHaveErrors(self): + + for node in DepthFirstIterator(self._scene.getRoot()): + + # valdiate only objects which can be selected because the settings per object + # can be applied only for them + if not node.isSelectable(): + continue + + hasErrors = self._checkStackForErrors(node.callDecoration("getStack")) + Application.getInstance().getObjectsModel().setStacksHaveErrors(hasErrors) + + #If any of models has an error then no reason check next objects on the build plate + if hasErrors: + break + + + def _checkStackForErrors(self, stack): + if stack is None: + return False + + for key in stack.getAllKeys(): + validation_state = stack.getProperty(key, "validationState") + if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError): + Logger.log("w", "Setting Per Object %s is not valid.", key) + return True + return False + + def subscribeForSettingValidation(self, setting_name): + selected_object = Selection.getSelectedObject(0) + stack = selected_object.callDecoration("getStack") # Don't try to get the active extruder since it may be None anyway. + if not stack: + return "" + + settings = stack.getTop() + setting_instance = settings.getInstance(setting_name) + if setting_instance: + setting_instance.propertyChanged.connect(self._onPropertyChanged) + + def unsubscribeForSettingValidation(self, setting_name): + selected_object = Selection.getSelectedObject(0) + stack = selected_object.callDecoration("getStack") # Don't try to get the active extruder since it may be None anyway. + if not stack: + return "" + + settings = stack.getTop() + setting_instance = settings.getInstance(setting_name) + if setting_instance: + setting_instance.propertyChanged.disconnect(self._onPropertyChanged)