Browse Source

Merge branch 'feature_model_check'

Ruben D 7 years ago
parent
commit
c8222b73c7

+ 116 - 0
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

+ 43 - 0
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"
+            }
+        }
+    }
+}

+ 14 - 0
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() }

+ 7 - 0
plugins/ModelChecker/model_checker.svg

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="30px" height="30px" viewBox="0 0 30 30" version="1.1" xmlns="http://www.w3.org/2000/svg">
+    <polygon fill="#000000" points="19 11 30 8 30 24 19 27" />
+    <path d="M10,19 C5.581722,19 2,15.418278 2,11 C2,6.581722 5.581722,3 10,3 C14.418278,3 18,6.581722 18,11 C18,15.418278 14.418278,19 10,19 Z M10,17 C13.3137085,17 16,14.3137085 16,11 C16,7.6862915 13.3137085,5 10,5 C6.6862915,5 4,7.6862915 4,11 C4,14.3137085 6.6862915,17 10,17 Z" fill="#000000" />
+    <polygon fill="#000000" points="4.2 15 6 16.8 1.8 21 0 19.2" />
+    <path d="M18.7333454,8.81666365 C18.2107269,6.71940704 16.9524304,4.91317986 15.248379,3.68790525 L18,3 L30,6 L18.7333454,8.81666365 Z M17,16.6573343 L17,27 L6,24 L6,19.0644804 C7.20495897,19.6632939 8.56315852,20 10,20 C12.8272661,20 15.3500445,18.6963331 17,16.6573343 Z" fill="#000000" />
+</svg>

+ 8 - 0
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"
+}

+ 2 - 1
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",

+ 13 - 0
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",

+ 36 - 1
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
+            }
+        }
+    }
+
 }

+ 2 - 0
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],