CURA-4557 it's now a small button next to the job spec when one of the warping materials is selected

This commit is contained in:
Jack Ha 2018-03-21 16:55:58 +01:00
parent 25bf7a6aba
commit d8d226c0d6
6 changed files with 190 additions and 57 deletions

View file

@ -1,13 +1,16 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import QTimer import os
from cura.Scene.CuraSceneNode import CuraSceneNode
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, pyqtProperty
from UM.Application import Application 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.Message import Message
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from UM.PluginRegistry import PluginRegistry
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
@ -17,35 +20,34 @@ SHRINKAGE_THRESHOLD = 0.5
WARNING_SIZE_XY = 150 WARNING_SIZE_XY = 150
WARNING_SIZE_Z = 100 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): def __init__(self):
super().__init__() 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 = [] 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 # Check node material shrinkage and bounding box size
for node in nodes_to_check: for node in nodes_to_check:
@ -64,7 +66,7 @@ class ModelChecker(Tool):
for node in DepthFirstIterator(scene.getRoot()): for node in DepthFirstIterator(scene.getRoot()):
if node.callDecoration("isSliceable"): if node.callDecoration("isSliceable"):
nodes_to_check.append(node) nodes_to_check.append(node)
return self.checkObjects(nodes_to_check) return self.checkObjectsForShrinkage(nodes_to_check)
## Display warning message ## Display warning message
def showWarningMessage(self, warning_nodes): def showWarningMessage(self, warning_nodes):
@ -82,23 +84,77 @@ class ModelChecker(Tool):
def showHappyMessage(self): def showHappyMessage(self):
happy_message = Message(catalog.i18nc( happy_message = Message(catalog.i18nc(
"@info:status", "@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, lifetime = 5,
title = catalog.i18nc("@info:title", "Model Checker")) title = catalog.i18nc("@info:title", "Model Checker"))
happy_message.show() happy_message.show()
def _onActiveToolChanged(self): ## Creates the view used by show popup. The view is saved because of the fairly aggressive garbage collection.
active_tool = self.getController().getActiveTool() def _createView(self):
if active_tool is None: Logger.log("d", "Creating model checker view.")
return
active_tool_id = active_tool.getPluginId() # Create the plugin dialog component
if active_tool_id != self.getPluginId(): path = os.path.join(PluginRegistry.getInstance().getPluginPath("ModelChecker"), "ModelChecker.qml")
self._last_known_tool_id = active_tool_id self._button_view = Application.getInstance().createQmlComponent(path, {"manager": self})
if active_tool_id == self.getPluginId():
warning_nodes = self.checkAllSliceableNodes() # The qml is only the button
if warning_nodes: Application.getInstance().addAdditionalComponent("jobSpecsButton", self._button_view)
self.showWarningMessage(warning_nodes)
else: Logger.log("d", "Model checker view created.")
self.showHappyMessage()
if self._last_known_tool_id is not None: def _onChanged(self, *args):
self.getController().setActiveTool(self._last_known_tool_id) 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

View file

@ -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"
}
}
}
}

View file

@ -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. # This example is released under the terms of the AGPLv3 or higher.
from . import ModelChecker from . import ModelChecker
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("uranium") i18n_catalog = i18nCatalog("cura")
def getMetaData(): def getMetaData():
return { 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
}
}
def register(app): def register(app):
return { "tool": ModelChecker.ModelChecker() } return { "extension": ModelChecker.ModelChecker() }

View file

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?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" xmlns:xlink="http://www.w3.org/1999/xlink"> <svg width="30px" height="30px" viewBox="0 0 30 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>ModelChecker</title> <title>ModelChecker</title>
<g id="ModelChecker" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="ModelChecker" stroke="none" fill="none">
<polygon id="Rectangle-3" fill="#000000" points="19 11 30 8 30 24 19 27"></polygon> <polygon fill="#000000" points="19 11 30 8 30 24 19 27"></polygon>
<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" id="Combined-Shape" fill="#000000"></path> <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" id="Combined-Shape" fill="#000000"></path>
<polygon id="Polygon" fill="#000000" points="4.2 15 6 16.8 1.8 21 0 19.2"></polygon> <polygon fill="#000000" points="4.2 15 6 16.8 1.8 21 0 19.2"></polygon>
<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" id="Combined-Shape" fill="#000000"></path> <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" id="Combined-Shape" fill="#000000"></path>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before After
Before After

View file

@ -124,15 +124,50 @@ Item {
} }
} }
Row {
id: additionalComponentsRow
anchors.top: jobNameRow.bottom
anchors.right: parent.right
}
Label Label
{ {
id: boundingSpec id: boundingSpec
anchors.top: jobNameRow.bottom 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 height: UM.Theme.getSize("jobspecs_line").height
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
font: UM.Theme.getFont("small") font: UM.Theme.getFont("small")
color: UM.Theme.getColor("text_scene") color: UM.Theme.getColor("text_scene")
text: CuraApplication.getSceneBoundingBoxString 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
}
}
}
} }

View file

@ -411,6 +411,8 @@
"save_button_save_to_button": [0.3, 2.7], "save_button_save_to_button": [0.3, 2.7],
"save_button_specs_icons": [1.4, 1.4], "save_button_specs_icons": [1.4, 1.4],
"job_specs_button": [2.7, 2.7],
"monitor_preheat_temperature_control": [4.5, 2.0], "monitor_preheat_temperature_control": [4.5, 2.0],
"modal_window_minimum": [60.0, 45], "modal_window_minimum": [60.0, 45],