diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py new file mode 100644 index 0000000000..1ceaa775d3 --- /dev/null +++ b/plugins/ModelChecker/ModelChecker.py @@ -0,0 +1,116 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import os + +from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, pyqtProperty + +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 +from UM.PluginRegistry import PluginRegistry +from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator + +catalog = i18nCatalog("cura") + + +class ModelChecker(QObject, Extension): + ## Signal that gets emitted when anything changed that we need to check. + onChanged = pyqtSignal() + + def __init__(self): + super().__init__() + + self._button_view = None + + 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._pluginsInitialized) + Application.getInstance().getController().getScene().sceneChanged.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): + 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 = [] + + # 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: + 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._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 + scene = Application.getInstance().getController().getScene() + for node in DepthFirstIterator(scene.getRoot()): + if node.callDecoration("isSliceable"): + yield node + + ## 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.") + + @pyqtProperty(bool, notify = onChanged) + def runChecks(self): + danger_shrinkage = self.checkObjectsForShrinkage() + + return any((danger_shrinkage, )) #If any of the checks fail, show the warning button. + + @pyqtSlot() + def showWarnings(self): + self._caution_message.show() + + 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 diff --git a/plugins/ModelChecker/ModelChecker.qml b/plugins/ModelChecker/ModelChecker.qml new file mode 100644 index 0000000000..8934b52eea --- /dev/null +++ b/plugins/ModelChecker/ModelChecker.qml @@ -0,0 +1,43 @@ +// 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.runChecks + tooltip: catalog.i18nc("@info:tooltip", "Check current setup for known problems.") + onClicked: manager.showWarnings() + + 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 new file mode 100644 index 0000000000..5f4d443729 --- /dev/null +++ b/plugins/ModelChecker/__init__.py @@ -0,0 +1,14 @@ +# 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("cura") + + +def getMetaData(): + return {} + +def register(app): + return { "extension": ModelChecker.ModelChecker() } diff --git a/plugins/ModelChecker/model_checker.svg b/plugins/ModelChecker/model_checker.svg new file mode 100644 index 0000000000..ce9594302e --- /dev/null +++ b/plugins/ModelChecker/model_checker.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/plugins/ModelChecker/plugin.json b/plugins/ModelChecker/plugin.json new file mode 100644 index 0000000000..a9190adcaa --- /dev/null +++ b/plugins/ModelChecker/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "Model Checker", + "author": "Ultimaker B.V.", + "version": "0.1", + "api": 4, + "description": "Checks models and print configuration for possible printing issues and give suggestions.", + "i18n-catalog": "cura" +} diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 5ff6838373..341f2bd3bb 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -983,7 +983,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 percentage": "material_shrinkage_percentage", } __unmapped_settings = [ "hardware compatible", diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 21ee543333..d7d9698439 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_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", diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 742e8d6765..3238c66a1e 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -115,15 +115,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],