From d0609e97e441573e32367306c0ba3ba0f7f235eb Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Thu, 15 Mar 2018 11:19:14 +0100 Subject: [PATCH 01/36] CURA-4557 added material_shrinkage_ratio_percentage setting --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 3 ++- resources/definitions/fdmprinter.def.json | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 8b17721794..76227f193a 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -988,7 +988,8 @@ class XmlMaterialProfile(InstanceContainer): "retraction amount": "retraction_amount", "retraction speed": "retraction_speed", "adhesion tendency": "material_adhesion_tendency", - "surface energy": "material_surface_energy" + "surface energy": "material_surface_energy", + "shrinkage ratio percentage": "material_shrinkage_ratio_percentage", } __unmapped_settings = [ "hardware compatible", diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 21ee543333..24699fa95c 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -2097,6 +2097,19 @@ "settable_per_mesh": false, "settable_per_extruder": true }, + "material_shrinkage_ratio_percentage": + { + "label": "Shrinkage Ratio", + "description": "Shrinkage ratio in percentage.", + "unit": "%", + "type": "float", + "default_value": 0, + "minimum_value": "0", + "maximum_value": "100", + "enabled": false, + "settable_per_mesh": false, + "settable_per_extruder": true + }, "material_flow": { "label": "Flow", From 50f9548da015f3bb24bfdf37b502294fbb01eed0 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Thu, 15 Mar 2018 14:33:31 +0100 Subject: [PATCH 02/36] CURA-4557 setting up plugin --- plugins/ModelChecker/ModelChecker.py | 38 ++++++++++++++++++++++++++++ plugins/ModelChecker/__init__.py | 23 +++++++++++++++++ plugins/ModelChecker/plugin.json | 8 ++++++ 3 files changed, 69 insertions(+) create mode 100644 plugins/ModelChecker/ModelChecker.py create mode 100644 plugins/ModelChecker/__init__.py create mode 100644 plugins/ModelChecker/plugin.json diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py new file mode 100644 index 0000000000..dedfc86938 --- /dev/null +++ b/plugins/ModelChecker/ModelChecker.py @@ -0,0 +1,38 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from PyQt5.QtCore import QTimer +from cura.Scene.CuraSceneNode import CuraSceneNode + +from UM.Application import Application +from UM.Extension import Extension +from UM.Logger import Logger + + +class ModelChecker(Extension): + def __init__(self): + super().__init__() + + self._update_timer = QTimer() + self._update_timer.setInterval(2000) + self._update_timer.setSingleShot(True) + self._update_timer.timeout.connect(self.checkObjects) + + self._nodes_to_check = set() + + ## Reacting to an event. ## + Application.getInstance().mainWindowChanged.connect(self.logMessage) #When the main window is created, log a message. + Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged) + + ## Adds a message to the log, as an example of how to listen to events. + def logMessage(self): + Logger.log("i", "This is an example log message. yeaaa") + + def checkObjects(self): + Logger.log("d", "############# checking....") + + def _onSceneChanged(self, source): + if isinstance(source, CuraSceneNode) and source.callDecoration("isSliceable"): + Logger.log("d", "triggurrrr") + self._nodes_to_check.add(source) + self._update_timer.start() diff --git a/plugins/ModelChecker/__init__.py b/plugins/ModelChecker/__init__.py new file mode 100644 index 0000000000..ae4aa7d0c0 --- /dev/null +++ b/plugins/ModelChecker/__init__.py @@ -0,0 +1,23 @@ +# Copyright (c) 2017 Ultimaker B.V. +# This example is released under the terms of the AGPLv3 or higher. + +from . import ModelChecker + +## Defines additional metadata for the plug-in. +# +# Some types of plug-ins require additional metadata, such as which file types +# they are able to read or the name of the tool they define. In the case of +# the "Extension" type plug-in, there is no additional metadata though. +def getMetaData(): + return {} + +## Lets Uranium know that this plug-in exists. +# +# This is called when starting the application to find out which plug-ins +# exist and what their types are. We need to return a dictionary mapping from +# strings representing plug-in types (in this case "extension") to objects +# that inherit from PluginObject. +# +# \param app The application that the plug-in needs to register with. +def register(app): + return {"extension": ModelChecker.ModelChecker()} diff --git a/plugins/ModelChecker/plugin.json b/plugins/ModelChecker/plugin.json new file mode 100644 index 0000000000..db8819a325 --- /dev/null +++ b/plugins/ModelChecker/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "Model Checker", + "author": "Ultimaker", + "version": "0.1", + "api": 4, + "description": "Checks models for possible printing issues and give suggestions.", + "catalog": "cura" +} From ef7139d3b2c8ba019fff43665e39c86161a8598a Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 19 Mar 2018 16:17:24 +0100 Subject: [PATCH 03/36] CURA-4557 First version of model checker --- plugins/ModelChecker/ModelChecker.py | 66 ++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index dedfc86938..764aaafcb8 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -6,7 +6,17 @@ from cura.Scene.CuraSceneNode import CuraSceneNode from UM.Application import Application from UM.Extension import Extension -from UM.Logger import Logger +from UM.Message import Message +from UM.i18n import i18nCatalog + +catalog = i18nCatalog("cura") + + +SHRINKAGE_THRESHOLD = 0.5 +WARNING_SIZE_XY = 150 +WARNING_SIZE_Z = 100 + +MESSAGE_LIFETIME = 10 class ModelChecker(Extension): @@ -14,25 +24,63 @@ class ModelChecker(Extension): super().__init__() self._update_timer = QTimer() - self._update_timer.setInterval(2000) + self._update_timer.setInterval(5000) self._update_timer.setSingleShot(True) self._update_timer.timeout.connect(self.checkObjects) self._nodes_to_check = set() + self._warning_model_names = set() # Collect the names of models so we show the next warning with timeout + ## Reacting to an event. ## - Application.getInstance().mainWindowChanged.connect(self.logMessage) #When the main window is created, log a message. Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged) - ## Adds a message to the log, as an example of how to listen to events. - def logMessage(self): - Logger.log("i", "This is an example log message. yeaaa") - def checkObjects(self): - Logger.log("d", "############# checking....") + warning_nodes = [] + global_container_stack = Application.getInstance().getGlobalContainerStack() + material_shrinkage = {} + need_check = False + + # Get all shrinkage values of materials used + for extruder_position, extruder in global_container_stack.extruders.items(): + shrinkage = extruder.material.getProperty("material_shrinkage_ratio_percentage", "value") + if shrinkage is None: + shrinkage = 0 + if shrinkage > SHRINKAGE_THRESHOLD: + need_check = True + material_shrinkage[extruder_position] = shrinkage + + # Check if we can bail out fast + if not need_check: + return + + # Check node material shrinkage and bounding box size + for node in self._nodes_to_check: + node_extruder_position = node.callDecoration("getActiveExtruderPosition") + if material_shrinkage[node_extruder_position] > SHRINKAGE_THRESHOLD: + bbox = node.getBoundingBox() + if bbox.width >= WARNING_SIZE_XY or bbox.depth >= WARNING_SIZE_XY or bbox.height >= WARNING_SIZE_Z: + warning_nodes.append(node) + + # Display warning message + if warning_nodes: + message_lifetime = MESSAGE_LIFETIME + for node in warning_nodes: + if node.getName() not in self._warning_model_names: + message_lifetime = 0 # infinite + self._warning_model_names.add(node.getName()) + caution_message = Message(catalog.i18nc( + "@info:warning", + "Some models may not be printed optimal due to object size and material chosen [%s].\n" + "Tips that may be useful to improve the print quality:\n" + "1) Use rounded corners\n" + "2) Turn the fan off (only if the are no tiny details on the model)\n" + "3) Use a different material") % ", ".join([n.getName() for n in warning_nodes]), + lifetime = message_lifetime, + title = catalog.i18nc("@info:title", "Model Warning")) + caution_message.show() def _onSceneChanged(self, source): if isinstance(source, CuraSceneNode) and source.callDecoration("isSliceable"): - Logger.log("d", "triggurrrr") self._nodes_to_check.add(source) self._update_timer.start() From e9585169136378edc771a61b851fa68708e97a0f Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 19 Mar 2018 16:50:35 +0100 Subject: [PATCH 04/36] CURA-4557 add checking on material change as well --- plugins/ModelChecker/ModelChecker.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index 764aaafcb8..b4c79387a8 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -8,6 +8,7 @@ from UM.Application import Application from UM.Extension import Extension from UM.Message import Message from UM.i18n import i18nCatalog +from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator catalog = i18nCatalog("cura") @@ -24,7 +25,7 @@ class ModelChecker(Extension): super().__init__() self._update_timer = QTimer() - self._update_timer.setInterval(5000) + self._update_timer.setInterval(2000) self._update_timer.setSingleShot(True) self._update_timer.timeout.connect(self.checkObjects) @@ -32,8 +33,11 @@ class ModelChecker(Extension): self._warning_model_names = set() # Collect the names of models so we show the next warning with timeout - ## Reacting to an event. ## + Application.getInstance().initializationFinished.connect(self.bindSignals) + + def bindSignals(self): Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged) + Application.getInstance().getMachineManager().rootMaterialChanged.connect(self._checkAllSliceableNodes) def checkObjects(self): warning_nodes = [] @@ -61,6 +65,7 @@ class ModelChecker(Extension): bbox = node.getBoundingBox() if bbox.width >= WARNING_SIZE_XY or bbox.depth >= WARNING_SIZE_XY or bbox.height >= WARNING_SIZE_Z: warning_nodes.append(node) + self._nodes_to_check = set() # Display warning message if warning_nodes: @@ -84,3 +89,12 @@ class ModelChecker(Extension): if isinstance(source, CuraSceneNode) and source.callDecoration("isSliceable"): self._nodes_to_check.add(source) self._update_timer.start() + + def _checkAllSliceableNodes(self, *args): + # Add all scene nodes + scene = Application.getInstance().getController().getScene() + for node in DepthFirstIterator(scene.getRoot()): + if node.callDecoration("isSliceable"): + self._nodes_to_check.add(node) + if self._nodes_to_check: + self._update_timer.start() From 1aba1cfe6ab319c31d17f215c77afa278bee258a Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Tue, 20 Mar 2018 16:30:11 +0100 Subject: [PATCH 05/36] CURA-4557 model checker is now a tool, click to activate --- plugins/ModelChecker/ModelChecker.py | 98 +++++++++++++++------------- plugins/ModelChecker/__init__.py | 30 ++++----- plugins/ModelChecker/plugin.json | 6 +- 3 files changed, 68 insertions(+), 66 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index b4c79387a8..0ae9cc5d01 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -5,7 +5,7 @@ from PyQt5.QtCore import QTimer from cura.Scene.CuraSceneNode import CuraSceneNode from UM.Application import Application -from UM.Extension import Extension +from UM.Tool import Tool from UM.Message import Message from UM.i18n import i18nCatalog from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator @@ -17,31 +17,20 @@ SHRINKAGE_THRESHOLD = 0.5 WARNING_SIZE_XY = 150 WARNING_SIZE_Z = 100 -MESSAGE_LIFETIME = 10 - -class ModelChecker(Extension): +class ModelChecker(Tool): def __init__(self): super().__init__() - self._update_timer = QTimer() - self._update_timer.setInterval(2000) - self._update_timer.setSingleShot(True) - self._update_timer.timeout.connect(self.checkObjects) + self._last_known_tool_id = None - self._nodes_to_check = set() + self._controller.activeToolChanged.connect(self._onActiveToolChanged) - self._warning_model_names = set() # Collect the names of models so we show the next warning with timeout - - Application.getInstance().initializationFinished.connect(self.bindSignals) - - def bindSignals(self): - Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged) - Application.getInstance().getMachineManager().rootMaterialChanged.connect(self._checkAllSliceableNodes) - - def checkObjects(self): + def checkObjects(self, nodes_to_check): warning_nodes = [] global_container_stack = Application.getInstance().getGlobalContainerStack() + if global_container_stack is None: + return [] material_shrinkage = {} need_check = False @@ -59,42 +48,57 @@ class ModelChecker(Extension): return # Check node material shrinkage and bounding box size - for node in self._nodes_to_check: + for node in nodes_to_check: node_extruder_position = node.callDecoration("getActiveExtruderPosition") if material_shrinkage[node_extruder_position] > SHRINKAGE_THRESHOLD: bbox = node.getBoundingBox() if bbox.width >= WARNING_SIZE_XY or bbox.depth >= WARNING_SIZE_XY or bbox.height >= WARNING_SIZE_Z: warning_nodes.append(node) - self._nodes_to_check = set() - # Display warning message - if warning_nodes: - message_lifetime = MESSAGE_LIFETIME - for node in warning_nodes: - if node.getName() not in self._warning_model_names: - message_lifetime = 0 # infinite - self._warning_model_names.add(node.getName()) - caution_message = Message(catalog.i18nc( - "@info:warning", - "Some models may not be printed optimal due to object size and material chosen [%s].\n" - "Tips that may be useful to improve the print quality:\n" - "1) Use rounded corners\n" - "2) Turn the fan off (only if the are no tiny details on the model)\n" - "3) Use a different material") % ", ".join([n.getName() for n in warning_nodes]), - lifetime = message_lifetime, - title = catalog.i18nc("@info:title", "Model Warning")) - caution_message.show() + return warning_nodes - def _onSceneChanged(self, source): - if isinstance(source, CuraSceneNode) and source.callDecoration("isSliceable"): - self._nodes_to_check.add(source) - self._update_timer.start() - - def _checkAllSliceableNodes(self, *args): - # Add all scene nodes + def checkAllSliceableNodes(self): + # Add all sliceable scene nodes to check scene = Application.getInstance().getController().getScene() + nodes_to_check = [] for node in DepthFirstIterator(scene.getRoot()): if node.callDecoration("isSliceable"): - self._nodes_to_check.add(node) - if self._nodes_to_check: - self._update_timer.start() + nodes_to_check.append(node) + return self.checkObjects(nodes_to_check) + + ## Display warning message + def showWarningMessage(self, warning_nodes): + caution_message = Message(catalog.i18nc( + "@info:status", + "Some models may not be printed optimal due to object size and material chosen [%s].\n" + "Tips that may be useful to improve the print quality:\n" + "1) Use rounded corners\n" + "2) Turn the fan off (only if the are no tiny details on the model)\n" + "3) Use a different material") % ", ".join([n.getName() for n in warning_nodes]), + lifetime = 0, + title = catalog.i18nc("@info:title", "Model Checker Warning")) + caution_message.show() + + def showHappyMessage(self): + happy_message = Message(catalog.i18nc( + "@info:status", + "The Model Checker did not detect any problems with your model / print setup combination."), + lifetime = 5, + title = catalog.i18nc("@info:title", "Model Checker")) + happy_message.show() + + def _onActiveToolChanged(self): + active_tool = self.getController().getActiveTool() + if active_tool is None: + return + active_tool_id = active_tool.getPluginId() + if active_tool_id != self.getPluginId(): + self._last_known_tool_id = active_tool_id + if active_tool_id == self.getPluginId(): + warning_nodes = self.checkAllSliceableNodes() + if warning_nodes: + self.showWarningMessage(warning_nodes) + else: + self.showHappyMessage() + if self._last_known_tool_id is not None: + self.getController().setActiveTool(self._last_known_tool_id) diff --git a/plugins/ModelChecker/__init__.py b/plugins/ModelChecker/__init__.py index ae4aa7d0c0..b2b8587a7a 100644 --- a/plugins/ModelChecker/__init__.py +++ b/plugins/ModelChecker/__init__.py @@ -3,21 +3,19 @@ from . import ModelChecker -## Defines additional metadata for the plug-in. -# -# Some types of plug-ins require additional metadata, such as which file types -# they are able to read or the name of the tool they define. In the case of -# the "Extension" type plug-in, there is no additional metadata though. -def getMetaData(): - return {} +from UM.i18n import i18nCatalog +i18n_catalog = i18nCatalog("uranium") + + +def getMetaData(): + return { + "tool": { + "name": i18n_catalog.i18nc("@label", "Model Checker"), + "description": i18n_catalog.i18nc("@info:tooltip", "Checks models and print configuration for possible printing issues and give suggestions."), + "icon": "model_checker.svg", + "weight": 10 + } + } -## Lets Uranium know that this plug-in exists. -# -# This is called when starting the application to find out which plug-ins -# exist and what their types are. We need to return a dictionary mapping from -# strings representing plug-in types (in this case "extension") to objects -# that inherit from PluginObject. -# -# \param app The application that the plug-in needs to register with. def register(app): - return {"extension": ModelChecker.ModelChecker()} + return { "tool": ModelChecker.ModelChecker() } diff --git a/plugins/ModelChecker/plugin.json b/plugins/ModelChecker/plugin.json index db8819a325..a9190adcaa 100644 --- a/plugins/ModelChecker/plugin.json +++ b/plugins/ModelChecker/plugin.json @@ -1,8 +1,8 @@ { "name": "Model Checker", - "author": "Ultimaker", + "author": "Ultimaker B.V.", "version": "0.1", "api": 4, - "description": "Checks models for possible printing issues and give suggestions.", - "catalog": "cura" + "description": "Checks models and print configuration for possible printing issues and give suggestions.", + "i18n-catalog": "cura" } From 9ea59c1976c7d440cb7b9f4fd23be069c73b254f Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Tue, 20 Mar 2018 17:07:03 +0100 Subject: [PATCH 06/36] CURA-4557 rename ratio percentage to percentage --- plugins/ModelChecker/ModelChecker.py | 2 +- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 2 +- resources/definitions/fdmprinter.def.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index 0ae9cc5d01..afe8a7e47d 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -36,7 +36,7 @@ class ModelChecker(Tool): # Get all shrinkage values of materials used for extruder_position, extruder in global_container_stack.extruders.items(): - shrinkage = extruder.material.getProperty("material_shrinkage_ratio_percentage", "value") + shrinkage = extruder.material.getProperty("material_shrinkage_percentage", "value") if shrinkage is None: shrinkage = 0 if shrinkage > SHRINKAGE_THRESHOLD: diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 229292fe01..341f2bd3bb 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -984,7 +984,7 @@ class XmlMaterialProfile(InstanceContainer): "retraction speed": "retraction_speed", "adhesion tendency": "material_adhesion_tendency", "surface energy": "material_surface_energy", - "shrinkage ratio percentage": "material_shrinkage_ratio_percentage", + "shrinkage percentage": "material_shrinkage_percentage", } __unmapped_settings = [ "hardware compatible", diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 24699fa95c..d7d9698439 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -2097,7 +2097,7 @@ "settable_per_mesh": false, "settable_per_extruder": true }, - "material_shrinkage_ratio_percentage": + "material_shrinkage_percentage": { "label": "Shrinkage Ratio", "description": "Shrinkage ratio in percentage.", From 25bf7a6ababeaee3428924a25bc2f748897db817 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 21 Mar 2018 13:11:42 +0100 Subject: [PATCH 07/36] CURA-4557 added icon --- plugins/ModelChecker/model_checker.svg | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 plugins/ModelChecker/model_checker.svg diff --git a/plugins/ModelChecker/model_checker.svg b/plugins/ModelChecker/model_checker.svg new file mode 100644 index 0000000000..ca5228155a --- /dev/null +++ b/plugins/ModelChecker/model_checker.svg @@ -0,0 +1,10 @@ + + + ModelChecker + + + + + + + From c6e5123c2df17a7354d804596330e165544f6e60 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 21 Mar 2018 13:23:33 +0100 Subject: [PATCH 08/36] Update the link so we can see how many people use it --- plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py index fd6c4680e8..66ee43209f 100644 --- a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py +++ b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py @@ -69,7 +69,7 @@ class FirmwareUpdateCheckerJob(Job): # If we do this in a cool way, the download url should be available in the JSON file if self._set_download_url_callback: - self._set_download_url_callback("https://ultimaker.com/en/resources/20500-upgrade-firmware") + self._set_download_url_callback("https://ultimaker.com/en/resources/23129-updating-the-firmware?utm_source=cura&utm_medium=software&utm_campaign=hw-update") message.actionTriggered.connect(self._callback) message.show() From 143ee07228235594ca5278f23c62d8606b3e75a1 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 21 Mar 2018 13:25:05 +0100 Subject: [PATCH 09/36] Fix quality_type "Extra coarse" -> "extra coarse" CURA-5129 --- .../VersionUpgrade32to33/VersionUpgrade32to33.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py b/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py index 620f367e25..e39266884d 100644 --- a/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py +++ b/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py @@ -127,6 +127,9 @@ class VersionUpgrade32to33(VersionUpgrade): parser["metadata"]["position"] = str(extruder_position) del parser["metadata"]["extruder"] + quality_type = parser["metadata"]["quality_type"] + parser["metadata"]["quality_type"] = quality_type.lower() + #Update version number. parser["general"]["version"] = "3" From da00bdf9c325e9e124ea1c20f351470bf01e3b4e Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 21 Mar 2018 13:50:56 +0100 Subject: [PATCH 10/36] Fix SimulationView onSceneChanged signal binding CURA-5130 --- plugins/SimulationView/SimulationView.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index 456d64e250..3697e38661 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -74,7 +74,7 @@ class SimulationView(View): self._global_container_stack = None self._proxy = SimulationViewProxy() - self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged) + self._controller.getScene().sceneChanged.connect(self._onSceneChanged) self._resetSettings() self._legend_items = None From 3eb9969d6db64172be406d416a8da2edc8924eb9 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 21 Mar 2018 15:08:34 +0100 Subject: [PATCH 11/36] CURA-4870 Make the Sync button react when the output device changed, otherwise can show a wrong value (not updated). --- resources/qml/Menus/ConfigurationMenu/SyncButton.qml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml b/resources/qml/Menus/ConfigurationMenu/SyncButton.qml index 8fe9dacf9a..6654708482 100644 --- a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml +++ b/resources/qml/Menus/ConfigurationMenu/SyncButton.qml @@ -98,4 +98,10 @@ Button target: Cura.MachineManager onCurrentConfigurationChanged: updateOnSync() } + + Connections + { + target: Cura.MachineManager + onOutputDevicesChanged: updateOnSync() + } } \ No newline at end of file From d8d226c0d654b3dbfc05e800785a013db8cc1642 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 21 Mar 2018 16:55:58 +0100 Subject: [PATCH 12/36] CURA-4557 it's now a small button next to the job spec when one of the warping materials is selected --- plugins/ModelChecker/ModelChecker.py | 140 +++++++++++++++++-------- plugins/ModelChecker/ModelChecker.qml | 47 +++++++++ plugins/ModelChecker/__init__.py | 15 +-- plugins/ModelChecker/model_checker.svg | 6 +- resources/qml/JobSpecs.qml | 37 ++++++- resources/themes/cura-light/theme.json | 2 + 6 files changed, 190 insertions(+), 57 deletions(-) create mode 100644 plugins/ModelChecker/ModelChecker.qml diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index afe8a7e47d..285002deda 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -1,13 +1,16 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QTimer -from cura.Scene.CuraSceneNode import CuraSceneNode +import os + +from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, pyqtProperty from UM.Application import Application -from UM.Tool import Tool +from UM.Extension import Extension +from UM.Logger import Logger from UM.Message import Message from UM.i18n import i18nCatalog +from UM.PluginRegistry import PluginRegistry from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator catalog = i18nCatalog("cura") @@ -17,35 +20,34 @@ SHRINKAGE_THRESHOLD = 0.5 WARNING_SIZE_XY = 150 WARNING_SIZE_Z = 100 +# Use this when actual shrinkage data is not in fdm_materials yet +MATERIALS_LOOKUP = { + "generic_abs": 1, + "generic_pc": 1, + "generic_pp": 1, + "generic_cpe_plus": 1 +} + + +class ModelChecker(QObject, Extension): + + needCheckChanged = pyqtSignal() -class ModelChecker(Tool): def __init__(self): super().__init__() - self._last_known_tool_id = None + self._button_view = None + self._need_checks = False - self._controller.activeToolChanged.connect(self._onActiveToolChanged) + Application.getInstance().initializationFinished.connect(self.bindSignals) + + def bindSignals(self): + Application.getInstance().getMachineManager().rootMaterialChanged.connect(self._onChanged) + + def checkObjectsForShrinkage(self, nodes_to_check): + material_shrinkage = self.getMaterialShrinkage() - def checkObjects(self, nodes_to_check): warning_nodes = [] - global_container_stack = Application.getInstance().getGlobalContainerStack() - if global_container_stack is None: - return [] - material_shrinkage = {} - need_check = False - - # Get all shrinkage values of materials used - for extruder_position, extruder in global_container_stack.extruders.items(): - shrinkage = extruder.material.getProperty("material_shrinkage_percentage", "value") - if shrinkage is None: - shrinkage = 0 - if shrinkage > SHRINKAGE_THRESHOLD: - need_check = True - material_shrinkage[extruder_position] = shrinkage - - # Check if we can bail out fast - if not need_check: - return # Check node material shrinkage and bounding box size for node in nodes_to_check: @@ -64,7 +66,7 @@ class ModelChecker(Tool): for node in DepthFirstIterator(scene.getRoot()): if node.callDecoration("isSliceable"): nodes_to_check.append(node) - return self.checkObjects(nodes_to_check) + return self.checkObjectsForShrinkage(nodes_to_check) ## Display warning message def showWarningMessage(self, warning_nodes): @@ -82,23 +84,77 @@ class ModelChecker(Tool): def showHappyMessage(self): happy_message = Message(catalog.i18nc( "@info:status", - "The Model Checker did not detect any problems with your model / print setup combination."), + "The Model Checker did not detect any problems with your model / chosen materials combination."), lifetime = 5, title = catalog.i18nc("@info:title", "Model Checker")) happy_message.show() - def _onActiveToolChanged(self): - active_tool = self.getController().getActiveTool() - if active_tool is None: - return - active_tool_id = active_tool.getPluginId() - if active_tool_id != self.getPluginId(): - self._last_known_tool_id = active_tool_id - if active_tool_id == self.getPluginId(): - warning_nodes = self.checkAllSliceableNodes() - if warning_nodes: - self.showWarningMessage(warning_nodes) - else: - self.showHappyMessage() - if self._last_known_tool_id is not None: - self.getController().setActiveTool(self._last_known_tool_id) + ## Creates the view used by show popup. The view is saved because of the fairly aggressive garbage collection. + def _createView(self): + Logger.log("d", "Creating model checker view.") + + # Create the plugin dialog component + path = os.path.join(PluginRegistry.getInstance().getPluginPath("ModelChecker"), "ModelChecker.qml") + self._button_view = Application.getInstance().createQmlComponent(path, {"manager": self}) + + # The qml is only the button + Application.getInstance().addAdditionalComponent("jobSpecsButton", self._button_view) + + Logger.log("d", "Model checker view created.") + + def _onChanged(self, *args): + if self._button_view is None: + self._createView() + old_need_checks = self._need_checks + self._need_checks = self.calculateNeedCheck() + if old_need_checks != self._need_checks: + self.needCheckChanged.emit() + + @pyqtSlot() + def runChecks(self): + warning_nodes = self.checkAllSliceableNodes() + if warning_nodes: + self.showWarningMessage(warning_nodes) + else: + self.showHappyMessage() + + # TODO: use this if branch feature_model_check is merged in fdm_materials to master + # def getMaterialShrinkage(self): + # global_container_stack = Application.getInstance().getGlobalContainerStack() + # if global_container_stack is None: + # return {} + # + # material_shrinkage = {} + # # Get all shrinkage values of materials used + # for extruder_position, extruder in global_container_stack.extruders.items(): + # shrinkage = extruder.material.getProperty("material_shrinkage_percentage", "value") + # if shrinkage is None: + # shrinkage = 0 + # material_shrinkage[extruder_position] = shrinkage + # return material_shrinkage + + def getMaterialShrinkage(self): + global_container_stack = Application.getInstance().getGlobalContainerStack() + if global_container_stack is None: + return {} + + material_shrinkage = {} + # Get all shrinkage values of materials used + for extruder_position, extruder in global_container_stack.extruders.items(): + base_file = extruder.material.getMetaDataEntry("base_file") + shrinkage = MATERIALS_LOOKUP.get(base_file, 0) + material_shrinkage[extruder_position] = shrinkage + return material_shrinkage + + @pyqtProperty(bool, notify = needCheckChanged) + def needCheck(self): + return self._need_checks + + def calculateNeedCheck(self): + need_check = False + + for shrinkage in self.getMaterialShrinkage().values(): + if shrinkage > SHRINKAGE_THRESHOLD: + need_check = True + + return need_check diff --git a/plugins/ModelChecker/ModelChecker.qml b/plugins/ModelChecker/ModelChecker.qml new file mode 100644 index 0000000000..77a483c2c6 --- /dev/null +++ b/plugins/ModelChecker/ModelChecker.qml @@ -0,0 +1,47 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.1 +import QtQuick.Window 2.2 + +import UM 1.2 as UM +import Cura 1.0 as Cura + + +Button +{ + id: modelCheckerButton + + UM.I18nCatalog{id: catalog; name:"cura"} + + visible: manager.needCheck + tooltip: catalog.i18nc("@info:tooltip", "Check current setup for known problems.") + onClicked: manager.runChecks() + + //anchors.leftMargin: UM.Theme.getSize("default_margin").width + + //anchors.right: parent.right + //anchors.verticalCenter: parent.verticalCenter + width: UM.Theme.getSize("save_button_specs_icons").width + height: UM.Theme.getSize("save_button_specs_icons").height + + style: ButtonStyle + { + background: Item + { + UM.RecolorImage + { + width: UM.Theme.getSize("save_button_specs_icons").width; + height: UM.Theme.getSize("save_button_specs_icons").height; + sourceSize.width: width; + sourceSize.height: width; + color: control.hovered ? UM.Theme.getColor("text_scene_hover") : UM.Theme.getColor("text_scene"); + source: "model_checker.svg" + } + } + } +} diff --git a/plugins/ModelChecker/__init__.py b/plugins/ModelChecker/__init__.py index b2b8587a7a..5f4d443729 100644 --- a/plugins/ModelChecker/__init__.py +++ b/plugins/ModelChecker/__init__.py @@ -1,21 +1,14 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # This example is released under the terms of the AGPLv3 or higher. from . import ModelChecker from UM.i18n import i18nCatalog -i18n_catalog = i18nCatalog("uranium") +i18n_catalog = i18nCatalog("cura") def getMetaData(): - return { - "tool": { - "name": i18n_catalog.i18nc("@label", "Model Checker"), - "description": i18n_catalog.i18nc("@info:tooltip", "Checks models and print configuration for possible printing issues and give suggestions."), - "icon": "model_checker.svg", - "weight": 10 - } - } + return {} def register(app): - return { "tool": ModelChecker.ModelChecker() } + return { "extension": ModelChecker.ModelChecker() } diff --git a/plugins/ModelChecker/model_checker.svg b/plugins/ModelChecker/model_checker.svg index ca5228155a..5b9dd4d197 100644 --- a/plugins/ModelChecker/model_checker.svg +++ b/plugins/ModelChecker/model_checker.svg @@ -1,10 +1,10 @@ ModelChecker - - + + - + diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 04e8ec397f..5cf6987f75 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -124,15 +124,50 @@ Item { } } + Row { + id: additionalComponentsRow + anchors.top: jobNameRow.bottom + anchors.right: parent.right + } + Label { id: boundingSpec anchors.top: jobNameRow.bottom - anchors.right: parent.right + anchors.right: additionalComponentsRow.left + anchors.rightMargin: + { + if (additionalComponentsRow.width > 0) + { + return UM.Theme.getSize("default_margin").width + } + else + { + return 0; + } + } height: UM.Theme.getSize("jobspecs_line").height verticalAlignment: Text.AlignVCenter font: UM.Theme.getFont("small") color: UM.Theme.getColor("text_scene") text: CuraApplication.getSceneBoundingBoxString } + + Component.onCompleted: { + base.addAdditionalComponents("jobSpecsButton") + } + + Connections { + target: CuraApplication + onAdditionalComponentsChanged: base.addAdditionalComponents("jobSpecsButton") + } + + function addAdditionalComponents (areaId) { + if(areaId == "jobSpecsButton") { + for (var component in CuraApplication.additionalComponents["jobSpecsButton"]) { + CuraApplication.additionalComponents["jobSpecsButton"][component].parent = additionalComponentsRow + } + } + } + } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 4f4b2306a8..0fde7f3bc9 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -411,6 +411,8 @@ "save_button_save_to_button": [0.3, 2.7], "save_button_specs_icons": [1.4, 1.4], + "job_specs_button": [2.7, 2.7], + "monitor_preheat_temperature_control": [4.5, 2.0], "modal_window_minimum": [60.0, 45], From 5e3cf921973e9e45ec4161ba86c662b17f8d290e Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 21 Mar 2018 16:57:48 +0100 Subject: [PATCH 13/36] CURA-5134 Add funtionality to lazily remove plugins --- cura/CuraApplication.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 98c682a8a3..1bbfa7be58 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -498,8 +498,13 @@ class CuraApplication(QtApplication): def getStaticVersion(cls): return CuraVersion + ## Handle removing the unneeded plugins + # \sa PluginRegistry + def _removePlugins(self): + self._plugin_registry.removePlugins() + ## Handle loading of all plugin types (and the backend explicitly) - # \sa PluginRegistery + # \sa PluginRegistry def _loadPlugins(self): self._plugin_registry.addType("profile_reader", self._addProfileReader) self._plugin_registry.addType("profile_writer", self._addProfileWriter) From 4226f3641b26ec233e751f662c11a3db64daa594 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 21 Mar 2018 17:12:36 +0100 Subject: [PATCH 14/36] Better defaults for filament change And better descriptions. --- plugins/PostProcessingPlugin/scripts/FilamentChange.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/FilamentChange.py b/plugins/PostProcessingPlugin/scripts/FilamentChange.py index 2bb7891634..07e887b082 100644 --- a/plugins/PostProcessingPlugin/scripts/FilamentChange.py +++ b/plugins/PostProcessingPlugin/scripts/FilamentChange.py @@ -27,18 +27,18 @@ class FilamentChange(Script): "initial_retract": { "label": "Initial Retraction", - "description": "Initial filament retraction distance", + "description": "Initial filament retraction distance. The filament will be retracted with this amount before moving the nozzle away from the ongoing print.", "unit": "mm", "type": "float", - "default_value": 300.0 + "default_value": 30.0 }, "later_retract": { "label": "Later Retraction Distance", - "description": "Later filament retraction distance for removal", + "description": "Later filament retraction distance for removal. The filament will be retracted all the way out of the printer so that you can change the filament.", "unit": "mm", "type": "float", - "default_value": 30.0 + "default_value": 300.0 } } }""" From 4fa195894de40d94787718be1bef21d05b5a5e3f Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Wed, 21 Mar 2018 17:21:11 +0100 Subject: [PATCH 15/36] Fix: After reloading merged models they do not update origin position CURA-4846 --- cura/CuraApplication.py | 61 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 98c682a8a3..aefadcba95 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1280,8 +1280,11 @@ class CuraApplication(QtApplication): def reloadAll(self): Logger.log("i", "Reloading all loaded mesh data.") nodes = [] + has_merged_nodes = False for node in DepthFirstIterator(self.getController().getScene().getRoot()): - if not isinstance(node, CuraSceneNode) or not node.getMeshData(): + if not isinstance(node, CuraSceneNode) or not node.getMeshData() : + if node.getName() == 'MergedMesh': + has_merged_nodes = True continue nodes.append(node) @@ -1295,10 +1298,14 @@ class CuraApplication(QtApplication): job = ReadMeshJob(file_name) job._node = node job.finished.connect(self._reloadMeshFinished) + if has_merged_nodes: + job.finished.connect(self.centerMergedMeshes) + job.start() else: Logger.log("w", "Unable to reload data because we don't have a filename.") + ## Get logging data of the backend engine # \returns \type{string} Logging data @pyqtSlot(result = str) @@ -1368,6 +1375,58 @@ class CuraApplication(QtApplication): # Use the previously found center of the group bounding box as the new location of the group group_node.setPosition(group_node.getBoundingBox().center) + group_node.setName("MergedMesh") # add a specific name to destinguis this node + + + ## Updates center position of all merged meshes + # \param jobNode \type{Job} empty object which passed which is required by JobQueue + def centerMergedMeshes(self, jobNode): + group_nodes = [] + for node in DepthFirstIterator(self.getController().getScene().getRoot()): + if isinstance(node, CuraSceneNode) and node.getName() == "MergedMesh": + + #checking by name might be not enough, the merged mesh should has "GroupDecorator" decorator + for decorator in node.getDecorators(): + if isinstance(decorator, GroupDecorator): + group_nodes.append(node) + break + + for group_node in group_nodes: + meshes = [node.getMeshData() for node in group_node.getAllChildren() if node.getMeshData()] + + # Compute the center of the objects + object_centers = [] + # Forget about the translation that the original objects have + zero_translation = Matrix(data=numpy.zeros(3)) + for mesh, node in zip(meshes, group_node.getChildren()): + transformation = node.getLocalTransformation() + transformation.setTranslation(zero_translation) + transformed_mesh = mesh.getTransformed(transformation) + center = transformed_mesh.getCenterPosition() + if center is not None: + object_centers.append(center) + + if object_centers and len(object_centers) > 0: + middle_x = sum([v.x for v in object_centers]) / len(object_centers) + middle_y = sum([v.y for v in object_centers]) / len(object_centers) + middle_z = sum([v.z for v in object_centers]) / len(object_centers) + offset = Vector(middle_x, middle_y, middle_z) + else: + offset = Vector(0, 0, 0) + + # Move each node to the same position. + for mesh, node in zip(meshes, group_node.getChildren()): + transformation = node.getLocalTransformation() + transformation.setTranslation(zero_translation) + transformed_mesh = mesh.getTransformed(transformation) + + # Align the object around its zero position + # and also apply the offset to center it inside the group. + node.setPosition(-transformed_mesh.getZeroPosition() - offset) + + # Use the previously found center of the group bounding box as the new location of the group + group_node.setPosition(group_node.getBoundingBox().center) + @pyqtSlot() def groupSelected(self): From 47417c86a3c7bafaea932ecbcdc078a231daba79 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Wed, 21 Mar 2018 17:25:23 +0100 Subject: [PATCH 16/36] Rename method name CURA-4846 --- cura/CuraApplication.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index aa5811041c..6d5bd34ee4 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1304,7 +1304,7 @@ class CuraApplication(QtApplication): job._node = node job.finished.connect(self._reloadMeshFinished) if has_merged_nodes: - job.finished.connect(self.centerMergedMeshes) + job.finished.connect(self.updateOriginOfMergedMeshes) job.start() else: @@ -1383,9 +1383,9 @@ class CuraApplication(QtApplication): group_node.setName("MergedMesh") # add a specific name to destinguis this node - ## Updates center position of all merged meshes + ## Updates origin position of all merged meshes # \param jobNode \type{Job} empty object which passed which is required by JobQueue - def centerMergedMeshes(self, jobNode): + def updateOriginOfMergedMeshes(self, jobNode): group_nodes = [] for node in DepthFirstIterator(self.getController().getScene().getRoot()): if isinstance(node, CuraSceneNode) and node.getName() == "MergedMesh": From 6646d898c23a612410c0fa74e52f4a741a24b782 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 00:14:58 +0100 Subject: [PATCH 17/36] Remove debug code This code was put in to allow debugging before the branches are merged. We should just assume that the two branches get merged at the same time. Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.py | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index 285002deda..9625b18b9b 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -20,14 +20,6 @@ SHRINKAGE_THRESHOLD = 0.5 WARNING_SIZE_XY = 150 WARNING_SIZE_Z = 100 -# Use this when actual shrinkage data is not in fdm_materials yet -MATERIALS_LOOKUP = { - "generic_abs": 1, - "generic_pc": 1, - "generic_pp": 1, - "generic_cpe_plus": 1 -} - class ModelChecker(QObject, Extension): @@ -118,21 +110,6 @@ class ModelChecker(QObject, Extension): else: self.showHappyMessage() - # TODO: use this if branch feature_model_check is merged in fdm_materials to master - # def getMaterialShrinkage(self): - # global_container_stack = Application.getInstance().getGlobalContainerStack() - # if global_container_stack is None: - # return {} - # - # material_shrinkage = {} - # # Get all shrinkage values of materials used - # for extruder_position, extruder in global_container_stack.extruders.items(): - # shrinkage = extruder.material.getProperty("material_shrinkage_percentage", "value") - # if shrinkage is None: - # shrinkage = 0 - # material_shrinkage[extruder_position] = shrinkage - # return material_shrinkage - def getMaterialShrinkage(self): global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack is None: @@ -141,8 +118,9 @@ class ModelChecker(QObject, Extension): material_shrinkage = {} # Get all shrinkage values of materials used for extruder_position, extruder in global_container_stack.extruders.items(): - base_file = extruder.material.getMetaDataEntry("base_file") - shrinkage = MATERIALS_LOOKUP.get(base_file, 0) + shrinkage = extruder.material.getProperty("material_shrinkage_percentage", "value") + if shrinkage is None: + shrinkage = 0 material_shrinkage[extruder_position] = shrinkage return material_shrinkage From c7ba9f9e3711985500f3738edcd14126d583fe22 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 00:18:12 +0100 Subject: [PATCH 18/36] Improve wording of warning for too large models A bit simpler English here. Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index 9625b18b9b..229eff1c7f 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -64,7 +64,7 @@ class ModelChecker(QObject, Extension): def showWarningMessage(self, warning_nodes): caution_message = Message(catalog.i18nc( "@info:status", - "Some models may not be printed optimal due to object size and material chosen [%s].\n" + "Some models may not be printed optimal due to object size and chosen material [%s].\n" "Tips that may be useful to improve the print quality:\n" "1) Use rounded corners\n" "2) Turn the fan off (only if the are no tiny details on the model)\n" From ab7f10ea52bb40e1c037f78717b5aa8b97a3b805 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 00:20:10 +0100 Subject: [PATCH 19/36] Make model checker warning easier to translate It's often more clear to have a variable name in there so that the translator knows what'll be filled in for the variable. Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index 229eff1c7f..8557fb8380 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -64,11 +64,11 @@ class ModelChecker(QObject, Extension): def showWarningMessage(self, warning_nodes): caution_message = Message(catalog.i18nc( "@info:status", - "Some models may not be printed optimal due to object size and chosen material [%s].\n" + "Some models may not be printed optimal due to object size and chosen material for models: {model_names}.\n" "Tips that may be useful to improve the print quality:\n" "1) Use rounded corners\n" "2) Turn the fan off (only if the are no tiny details on the model)\n" - "3) Use a different material") % ", ".join([n.getName() for n in warning_nodes]), + "3) Use a different material").format(model_names = ", ".join([n.getName() for n in warning_nodes])), lifetime = 0, title = catalog.i18nc("@info:title", "Model Checker Warning")) caution_message.show() From 37edce597681ee206bc1b45f86f7f616f784ecd3 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 00:28:00 +0100 Subject: [PATCH 20/36] Reuse message instances Slightly more performant. And now if you click on the button again you won't get a spammy new message each time. Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index 8557fb8380..f5b5549575 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -31,6 +31,15 @@ class ModelChecker(QObject, Extension): self._button_view = None self._need_checks = False + self._happy_message = Message(catalog.i18nc( + "@info:status", + "The Model Checker did not detect any problems with your model / chosen materials combination."), + lifetime = 5, + title = catalog.i18nc("@info:title", "Model Checker")) + self._caution_message = Message("", #Message text gets set when the message gets shown, to display the models in question. + lifetime = 0, + title = catalog.i18nc("@info:title", "Model Checker Warning")) + Application.getInstance().initializationFinished.connect(self.bindSignals) def bindSignals(self): @@ -62,24 +71,17 @@ class ModelChecker(QObject, Extension): ## Display warning message def showWarningMessage(self, warning_nodes): - caution_message = Message(catalog.i18nc( + self._caution_message.setText(catalog.i18nc( "@info:status", "Some models may not be printed optimal due to object size and chosen material for models: {model_names}.\n" "Tips that may be useful to improve the print quality:\n" "1) Use rounded corners\n" "2) Turn the fan off (only if the are no tiny details on the model)\n" - "3) Use a different material").format(model_names = ", ".join([n.getName() for n in warning_nodes])), - lifetime = 0, - title = catalog.i18nc("@info:title", "Model Checker Warning")) - caution_message.show() + "3) Use a different material").format(model_names = ", ".join([n.getName() for n in warning_nodes]))) + self._caution_message.show() def showHappyMessage(self): - happy_message = Message(catalog.i18nc( - "@info:status", - "The Model Checker did not detect any problems with your model / chosen materials combination."), - lifetime = 5, - title = catalog.i18nc("@info:title", "Model Checker")) - happy_message.show() + self._happy_message.show() ## Creates the view used by show popup. The view is saved because of the fairly aggressive garbage collection. def _createView(self): From 55b247abbdf13e5e412237c5243f502c18b66424 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 00:32:46 +0100 Subject: [PATCH 21/36] Hide outdated message if checking again And if the result is different. Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index f5b5549575..58c7e380bf 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -71,6 +71,7 @@ class ModelChecker(QObject, Extension): ## Display warning message def showWarningMessage(self, warning_nodes): + self._happy_message.hide() self._caution_message.setText(catalog.i18nc( "@info:status", "Some models may not be printed optimal due to object size and chosen material for models: {model_names}.\n" @@ -81,6 +82,7 @@ class ModelChecker(QObject, Extension): self._caution_message.show() def showHappyMessage(self): + self._caution_message.hide() self._happy_message.show() ## Creates the view used by show popup. The view is saved because of the fairly aggressive garbage collection. From f91abf042f49ad4122e6f47c3ae085c99bde10c5 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 00:35:55 +0100 Subject: [PATCH 22/36] Add documentation for globals And maybe they should not be globals. Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index 58c7e380bf..b1d61416b9 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -16,9 +16,9 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator catalog = i18nCatalog("cura") -SHRINKAGE_THRESHOLD = 0.5 -WARNING_SIZE_XY = 150 -WARNING_SIZE_Z = 100 +SHRINKAGE_THRESHOLD = 0.5 #From what shrinkage percentage a warning will be issued about the model size. +WARNING_SIZE_XY = 150 #The horizontal size of a model that would be too large when dealing with shrinking materials. +WARNING_SIZE_Z = 100 #The vertical size of a model that would be too large when dealing with shrinking materials. class ModelChecker(QObject, Extension): From 2db208cc758690269b792a4096e0590987567de0 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 00:38:39 +0100 Subject: [PATCH 23/36] Remove unused code The IDs, group, and xlink and such are not used at all. Contributes to issue CURA-4557. --- plugins/ModelChecker/model_checker.svg | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/plugins/ModelChecker/model_checker.svg b/plugins/ModelChecker/model_checker.svg index 5b9dd4d197..ce9594302e 100644 --- a/plugins/ModelChecker/model_checker.svg +++ b/plugins/ModelChecker/model_checker.svg @@ -1,10 +1,7 @@ - - ModelChecker - - - - - - + + + + + From 7ec7136a3ad7ca2e48845c4198182af255e58066 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 00:39:07 +0100 Subject: [PATCH 24/36] Remove debug code Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.qml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.qml b/plugins/ModelChecker/ModelChecker.qml index 77a483c2c6..9ed023c6ac 100644 --- a/plugins/ModelChecker/ModelChecker.qml +++ b/plugins/ModelChecker/ModelChecker.qml @@ -22,10 +22,6 @@ Button tooltip: catalog.i18nc("@info:tooltip", "Check current setup for known problems.") onClicked: manager.runChecks() - //anchors.leftMargin: UM.Theme.getSize("default_margin").width - - //anchors.right: parent.right - //anchors.verticalCenter: parent.verticalCenter width: UM.Theme.getSize("save_button_specs_icons").width height: UM.Theme.getSize("save_button_specs_icons").height From f5c1e59166d2b44b1f311ffd79735320acba6920 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 00:46:29 +0100 Subject: [PATCH 25/36] Call checkObjectsForShrinkage directly from runChecks I'd like to transition to a state where you can read in runChecks all the checks that are being run. The former function checkAllSliceableNodes is now a helper function to list sliceable nodes. Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index b1d61416b9..8c26c24619 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -45,13 +45,13 @@ class ModelChecker(QObject, Extension): def bindSignals(self): Application.getInstance().getMachineManager().rootMaterialChanged.connect(self._onChanged) - def checkObjectsForShrinkage(self, nodes_to_check): + def checkObjectsForShrinkage(self): material_shrinkage = self.getMaterialShrinkage() warning_nodes = [] # Check node material shrinkage and bounding box size - for node in nodes_to_check: + for node in self.sliceableNodes(): node_extruder_position = node.callDecoration("getActiveExtruderPosition") if material_shrinkage[node_extruder_position] > SHRINKAGE_THRESHOLD: bbox = node.getBoundingBox() @@ -60,14 +60,12 @@ class ModelChecker(QObject, Extension): return warning_nodes - def checkAllSliceableNodes(self): + def sliceableNodes(self): # Add all sliceable scene nodes to check scene = Application.getInstance().getController().getScene() - nodes_to_check = [] for node in DepthFirstIterator(scene.getRoot()): if node.callDecoration("isSliceable"): - nodes_to_check.append(node) - return self.checkObjectsForShrinkage(nodes_to_check) + yield node ## Display warning message def showWarningMessage(self, warning_nodes): @@ -108,7 +106,7 @@ class ModelChecker(QObject, Extension): @pyqtSlot() def runChecks(self): - warning_nodes = self.checkAllSliceableNodes() + warning_nodes = self.checkObjectsForShrinkage() if warning_nodes: self.showWarningMessage(warning_nodes) else: From 987b475e3b88090050b40b706ead9a97b3ba0456 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 00:48:23 +0100 Subject: [PATCH 26/36] Remove unused parameter Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index 8c26c24619..d1bdeeb328 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -96,7 +96,7 @@ class ModelChecker(QObject, Extension): Logger.log("d", "Model checker view created.") - def _onChanged(self, *args): + def _onChanged(self): if self._button_view is None: self._createView() old_need_checks = self._need_checks From 5bf93a8398974502c7ddaa5644cf3133a3d19a3a Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 01:31:35 +0100 Subject: [PATCH 27/36] Only display plug-in checker when there's really a warning As discussed with LukeChen-Ultimaker, we only want to display a button when there is really something up with the current set-up, not when there might be something up. Most users will be confused to see a button that always says that everything is fine. This also simplifies the signal handling a lot. Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.py | 73 +++++++++++++-------------- plugins/ModelChecker/ModelChecker.qml | 4 +- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index d1bdeeb328..ab8154cfec 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -22,14 +22,14 @@ WARNING_SIZE_Z = 100 #The vertical size of a model that would be too large when class ModelChecker(QObject, Extension): - - needCheckChanged = pyqtSignal() + ## Signal that gets emitted when anything changed that we need to check. + onChanged = pyqtSignal() def __init__(self): super().__init__() self._button_view = None - self._need_checks = False + self._has_warnings = False self._happy_message = Message(catalog.i18nc( "@info:status", @@ -40,10 +40,20 @@ class ModelChecker(QObject, Extension): lifetime = 0, title = catalog.i18nc("@info:title", "Model Checker Warning")) - Application.getInstance().initializationFinished.connect(self.bindSignals) + Application.getInstance().initializationFinished.connect(self._pluginsInitialized) + Application.getInstance().getController().getScene().sceneChanged.connect(self._onChanged) - def bindSignals(self): - Application.getInstance().getMachineManager().rootMaterialChanged.connect(self._onChanged) + ## Pass-through to allow UM.Signal to connect with a pyqtSignal. + def _onChanged(self, _): + self.onChanged.emit() + + ## Called when plug-ins are initialized. + # + # This makes sure that we listen to changes of the material and that the + # button is created that indicates warnings with the current set-up. + def _pluginsInitialized(self): + Application.getInstance().getMachineManager().rootMaterialChanged.connect(self.onChanged) + self._createView() def checkObjectsForShrinkage(self): material_shrinkage = self.getMaterialShrinkage() @@ -68,15 +78,8 @@ class ModelChecker(QObject, Extension): yield node ## Display warning message - def showWarningMessage(self, warning_nodes): + def showWarningMessage(self, ): self._happy_message.hide() - self._caution_message.setText(catalog.i18nc( - "@info:status", - "Some models may not be printed optimal due to object size and chosen material for models: {model_names}.\n" - "Tips that may be useful to improve the print quality:\n" - "1) Use rounded corners\n" - "2) Turn the fan off (only if the are no tiny details on the model)\n" - "3) Use a different material").format(model_names = ", ".join([n.getName() for n in warning_nodes]))) self._caution_message.show() def showHappyMessage(self): @@ -96,19 +99,28 @@ class ModelChecker(QObject, Extension): Logger.log("d", "Model checker view created.") - def _onChanged(self): - if self._button_view is None: - self._createView() - old_need_checks = self._need_checks - self._need_checks = self.calculateNeedCheck() - if old_need_checks != self._need_checks: - self.needCheckChanged.emit() - - @pyqtSlot() + @pyqtProperty(bool, notify = onChanged) def runChecks(self): warning_nodes = self.checkObjectsForShrinkage() if warning_nodes: - self.showWarningMessage(warning_nodes) + self._caution_message.setText(catalog.i18nc( + "@info:status", + "Some models may not be printed optimal due to object size and chosen material for models: {model_names}.\n" + "Tips that may be useful to improve the print quality:\n" + "1) Use rounded corners\n" + "2) Turn the fan off (only if the are no tiny details on the model)\n" + "3) Use a different material").format(model_names = ", ".join([n.getName() for n in warning_nodes]))) + return True + else: + return False + + @pyqtSlot() + def showWarnings(self): + if not self._button_view: + self._createView() + + if self._has_warnings: + self.showWarningMessage() else: self.showHappyMessage() @@ -125,16 +137,3 @@ class ModelChecker(QObject, Extension): shrinkage = 0 material_shrinkage[extruder_position] = shrinkage return material_shrinkage - - @pyqtProperty(bool, notify = needCheckChanged) - def needCheck(self): - return self._need_checks - - def calculateNeedCheck(self): - need_check = False - - for shrinkage in self.getMaterialShrinkage().values(): - if shrinkage > SHRINKAGE_THRESHOLD: - need_check = True - - return need_check diff --git a/plugins/ModelChecker/ModelChecker.qml b/plugins/ModelChecker/ModelChecker.qml index 9ed023c6ac..8934b52eea 100644 --- a/plugins/ModelChecker/ModelChecker.qml +++ b/plugins/ModelChecker/ModelChecker.qml @@ -18,9 +18,9 @@ Button UM.I18nCatalog{id: catalog; name:"cura"} - visible: manager.needCheck + visible: manager.runChecks tooltip: catalog.i18nc("@info:tooltip", "Check current setup for known problems.") - onClicked: manager.runChecks() + onClicked: manager.showWarnings() width: UM.Theme.getSize("save_button_specs_icons").width height: UM.Theme.getSize("save_button_specs_icons").height From 15ad528aa54aa35d45f8bba2d397f64e7b84c5e4 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 01:34:04 +0100 Subject: [PATCH 28/36] Remove happy message Since the button can only get shown when there is something wrong, the happy message can never be shown any more. Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index ab8154cfec..60804be28b 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -31,11 +31,6 @@ class ModelChecker(QObject, Extension): self._button_view = None self._has_warnings = False - self._happy_message = Message(catalog.i18nc( - "@info:status", - "The Model Checker did not detect any problems with your model / chosen materials combination."), - lifetime = 5, - title = catalog.i18nc("@info:title", "Model Checker")) self._caution_message = Message("", #Message text gets set when the message gets shown, to display the models in question. lifetime = 0, title = catalog.i18nc("@info:title", "Model Checker Warning")) @@ -77,15 +72,6 @@ class ModelChecker(QObject, Extension): if node.callDecoration("isSliceable"): yield node - ## Display warning message - def showWarningMessage(self, ): - self._happy_message.hide() - self._caution_message.show() - - def showHappyMessage(self): - self._caution_message.hide() - self._happy_message.show() - ## Creates the view used by show popup. The view is saved because of the fairly aggressive garbage collection. def _createView(self): Logger.log("d", "Creating model checker view.") @@ -120,9 +106,7 @@ class ModelChecker(QObject, Extension): self._createView() if self._has_warnings: - self.showWarningMessage() - else: - self.showHappyMessage() + self.self._caution_message.show() def getMaterialShrinkage(self): global_container_stack = Application.getInstance().getGlobalContainerStack() From 25e7cb457d400aac0fc2308692401d131bfa8957 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 01:35:35 +0100 Subject: [PATCH 29/36] No need to create view again upon showing warnings It is now already done when initialization is completed. Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index 60804be28b..8e27171d68 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -102,9 +102,6 @@ class ModelChecker(QObject, Extension): @pyqtSlot() def showWarnings(self): - if not self._button_view: - self._createView() - if self._has_warnings: self.self._caution_message.show() From 329a0b121d8831c47f730c8db72d5654b90d4a74 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 01:38:34 +0100 Subject: [PATCH 30/36] Move shrinkage parameters into shrinkage function So that they are closer to where they are relevant if we're going to have more checks in this class. Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index 8e27171d68..5f3f7a8430 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -16,11 +16,6 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator catalog = i18nCatalog("cura") -SHRINKAGE_THRESHOLD = 0.5 #From what shrinkage percentage a warning will be issued about the model size. -WARNING_SIZE_XY = 150 #The horizontal size of a model that would be too large when dealing with shrinking materials. -WARNING_SIZE_Z = 100 #The vertical size of a model that would be too large when dealing with shrinking materials. - - class ModelChecker(QObject, Extension): ## Signal that gets emitted when anything changed that we need to check. onChanged = pyqtSignal() @@ -51,6 +46,10 @@ class ModelChecker(QObject, Extension): self._createView() def checkObjectsForShrinkage(self): + shrinkage_threshold = 0.5 #From what shrinkage percentage a warning will be issued about the model size. + warning_size_xy = 150 #The horizontal size of a model that would be too large when dealing with shrinking materials. + warning_size_z = 100 #The vertical size of a model that would be too large when dealing with shrinking materials. + material_shrinkage = self.getMaterialShrinkage() warning_nodes = [] @@ -58,9 +57,9 @@ class ModelChecker(QObject, Extension): # Check node material shrinkage and bounding box size for node in self.sliceableNodes(): node_extruder_position = node.callDecoration("getActiveExtruderPosition") - if material_shrinkage[node_extruder_position] > SHRINKAGE_THRESHOLD: + if material_shrinkage[node_extruder_position] > shrinkage_threshold: bbox = node.getBoundingBox() - if bbox.width >= WARNING_SIZE_XY or bbox.depth >= WARNING_SIZE_XY or bbox.height >= WARNING_SIZE_Z: + if bbox.width >= warning_size_xy or bbox.depth >= warning_size_xy or bbox.height >= warning_size_z: warning_nodes.append(node) return warning_nodes From 8e6ee411241cf54430f8bcd35a8afad1386f5b55 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 01:43:06 +0100 Subject: [PATCH 31/36] Let shrinkage check set its own message Another attempt to keep the shrinkage check contained to functions related to shrinkage more. Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index 5f3f7a8430..e5a09f7ea1 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -62,7 +62,15 @@ class ModelChecker(QObject, Extension): if bbox.width >= warning_size_xy or bbox.depth >= warning_size_xy or bbox.height >= warning_size_z: warning_nodes.append(node) - return warning_nodes + self._caution_message.setText(catalog.i18nc( + "@info:status", + "Some models may not be printed optimal due to object size and chosen material for models: {model_names}.\n" + "Tips that may be useful to improve the print quality:\n" + "1) Use rounded corners\n" + "2) Turn the fan off (only if the are no tiny details on the model)\n" + "3) Use a different material").format(model_names = ", ".join([n.getName() for n in warning_nodes]))) + + return len(warning_nodes) > 0 def sliceableNodes(self): # Add all sliceable scene nodes to check @@ -86,18 +94,9 @@ class ModelChecker(QObject, Extension): @pyqtProperty(bool, notify = onChanged) def runChecks(self): - warning_nodes = self.checkObjectsForShrinkage() - if warning_nodes: - self._caution_message.setText(catalog.i18nc( - "@info:status", - "Some models may not be printed optimal due to object size and chosen material for models: {model_names}.\n" - "Tips that may be useful to improve the print quality:\n" - "1) Use rounded corners\n" - "2) Turn the fan off (only if the are no tiny details on the model)\n" - "3) Use a different material").format(model_names = ", ".join([n.getName() for n in warning_nodes]))) - return True - else: - return False + danger_shrinkage = self.checkObjectsForShrinkage() + + return any((danger_shrinkage, )) #If any of the checks fail, show the warning button. @pyqtSlot() def showWarnings(self): From 5d51b75f4d604667e0418820985df673abf2ebaf Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 01:56:12 +0100 Subject: [PATCH 32/36] Remove unnecessary _has_warnings We just always show the message because the button can only be pressed while there are warnings. Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index e5a09f7ea1..1ceaa775d3 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -24,7 +24,6 @@ class ModelChecker(QObject, Extension): super().__init__() self._button_view = None - self._has_warnings = False self._caution_message = Message("", #Message text gets set when the message gets shown, to display the models in question. lifetime = 0, @@ -100,8 +99,7 @@ class ModelChecker(QObject, Extension): @pyqtSlot() def showWarnings(self): - if self._has_warnings: - self.self._caution_message.show() + self._caution_message.show() def getMaterialShrinkage(self): global_container_stack = Application.getInstance().getGlobalContainerStack() From 58205fb9653a4c6ffdd81685b2a62cac6313d852 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 22 Mar 2018 01:59:49 +0100 Subject: [PATCH 33/36] Make _getMaterialShrinkage protected No need to expose this. Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index 1ceaa775d3..8a501ceb27 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -49,7 +49,7 @@ class ModelChecker(QObject, Extension): warning_size_xy = 150 #The horizontal size of a model that would be too large when dealing with shrinking materials. warning_size_z = 100 #The vertical size of a model that would be too large when dealing with shrinking materials. - material_shrinkage = self.getMaterialShrinkage() + material_shrinkage = self._getMaterialShrinkage() warning_nodes = [] @@ -101,7 +101,7 @@ class ModelChecker(QObject, Extension): def showWarnings(self): self._caution_message.show() - def getMaterialShrinkage(self): + def _getMaterialShrinkage(self): global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack is None: return {} From 2fad098f0b2923855cb9755f82c137b157afc37f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 22 Mar 2018 09:11:47 +0100 Subject: [PATCH 34/36] Adjust description of button You now no longer click to check if there's something wrong. Contributes to issue CURA-4557. --- plugins/ModelChecker/ModelChecker.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ModelChecker/ModelChecker.qml b/plugins/ModelChecker/ModelChecker.qml index 8934b52eea..3db54d4387 100644 --- a/plugins/ModelChecker/ModelChecker.qml +++ b/plugins/ModelChecker/ModelChecker.qml @@ -19,7 +19,7 @@ Button UM.I18nCatalog{id: catalog; name:"cura"} visible: manager.runChecks - tooltip: catalog.i18nc("@info:tooltip", "Check current setup for known problems.") + tooltip: catalog.i18nc("@info:tooltip", "Some things could be problematic in this print. Click to see tips for adjustment.") onClicked: manager.showWarnings() width: UM.Theme.getSize("save_button_specs_icons").width From ce0e3f89b707cf7cafd769b09839e8a9815736ad Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 22 Mar 2018 09:42:11 +0100 Subject: [PATCH 35/36] Fix code style and typo CURA-4846 --- cura/CuraApplication.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 6d5bd34ee4..56f1528b9b 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1288,7 +1288,7 @@ class CuraApplication(QtApplication): has_merged_nodes = False for node in DepthFirstIterator(self.getController().getScene().getRoot()): if not isinstance(node, CuraSceneNode) or not node.getMeshData() : - if node.getName() == 'MergedMesh': + if node.getName() == "MergedMesh": has_merged_nodes = True continue @@ -1380,7 +1380,7 @@ class CuraApplication(QtApplication): # Use the previously found center of the group bounding box as the new location of the group group_node.setPosition(group_node.getBoundingBox().center) - group_node.setName("MergedMesh") # add a specific name to destinguis this node + group_node.setName("MergedMesh") # add a specific name to distinguish this node ## Updates origin position of all merged meshes From e2478b636ae59a59d180f2f9912aac6c978c8964 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 22 Mar 2018 09:49:22 +0100 Subject: [PATCH 36/36] Typo --- cura/CrashHandler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index f51174aec0..7700ee2e71 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -85,7 +85,7 @@ class CrashHandler: dialog = QDialog() dialog.setMinimumWidth(500) dialog.setMinimumHeight(170) - dialog.setWindowTitle(catalog.i18nc("@title:window", "Cura can't startup")) + dialog.setWindowTitle(catalog.i18nc("@title:window", "Cura can't start")) dialog.finished.connect(self._closeEarlyCrashDialog) layout = QVBoxLayout(dialog)