CURA-4557 model checker is now a tool, click to activate

This commit is contained in:
Jack Ha 2018-03-20 16:30:11 +01:00
parent e958516913
commit 1aba1cfe6a
3 changed files with 68 additions and 66 deletions

View file

@ -5,7 +5,7 @@ from PyQt5.QtCore import QTimer
from cura.Scene.CuraSceneNode import CuraSceneNode from cura.Scene.CuraSceneNode import CuraSceneNode
from UM.Application import Application from UM.Application import Application
from UM.Extension import Extension from UM.Tool import Tool
from UM.Message import Message from UM.Message import Message
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
@ -17,31 +17,20 @@ SHRINKAGE_THRESHOLD = 0.5
WARNING_SIZE_XY = 150 WARNING_SIZE_XY = 150
WARNING_SIZE_Z = 100 WARNING_SIZE_Z = 100
MESSAGE_LIFETIME = 10
class ModelChecker(Tool):
class ModelChecker(Extension):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self._update_timer = QTimer() self._last_known_tool_id = None
self._update_timer.setInterval(2000)
self._update_timer.setSingleShot(True)
self._update_timer.timeout.connect(self.checkObjects)
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 def checkObjects(self, nodes_to_check):
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 = [] warning_nodes = []
global_container_stack = Application.getInstance().getGlobalContainerStack() global_container_stack = Application.getInstance().getGlobalContainerStack()
if global_container_stack is None:
return []
material_shrinkage = {} material_shrinkage = {}
need_check = False need_check = False
@ -59,42 +48,57 @@ class ModelChecker(Extension):
return return
# Check node material shrinkage and bounding box size # 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") 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() 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) warning_nodes.append(node)
self._nodes_to_check = set()
# Display warning message return warning_nodes
if warning_nodes:
message_lifetime = MESSAGE_LIFETIME def checkAllSliceableNodes(self):
for node in warning_nodes: # Add all sliceable scene nodes to check
if node.getName() not in self._warning_model_names: scene = Application.getInstance().getController().getScene()
message_lifetime = 0 # infinite nodes_to_check = []
self._warning_model_names.add(node.getName()) for node in DepthFirstIterator(scene.getRoot()):
if node.callDecoration("isSliceable"):
nodes_to_check.append(node)
return self.checkObjects(nodes_to_check)
## Display warning message
def showWarningMessage(self, warning_nodes):
caution_message = Message(catalog.i18nc( caution_message = Message(catalog.i18nc(
"@info:warning", "@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 material chosen [%s].\n"
"Tips that may be useful to improve the print quality:\n" "Tips that may be useful to improve the print quality:\n"
"1) Use rounded corners\n" "1) Use rounded corners\n"
"2) Turn the fan off (only if the are no tiny details on the model)\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") % ", ".join([n.getName() for n in warning_nodes]),
lifetime = message_lifetime, lifetime = 0,
title = catalog.i18nc("@info:title", "Model Warning")) title = catalog.i18nc("@info:title", "Model Checker Warning"))
caution_message.show() caution_message.show()
def _onSceneChanged(self, source): def showHappyMessage(self):
if isinstance(source, CuraSceneNode) and source.callDecoration("isSliceable"): happy_message = Message(catalog.i18nc(
self._nodes_to_check.add(source) "@info:status",
self._update_timer.start() "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 _checkAllSliceableNodes(self, *args): def _onActiveToolChanged(self):
# Add all scene nodes active_tool = self.getController().getActiveTool()
scene = Application.getInstance().getController().getScene() if active_tool is None:
for node in DepthFirstIterator(scene.getRoot()): return
if node.callDecoration("isSliceable"): active_tool_id = active_tool.getPluginId()
self._nodes_to_check.add(node) if active_tool_id != self.getPluginId():
if self._nodes_to_check: self._last_known_tool_id = active_tool_id
self._update_timer.start() 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)

View file

@ -3,21 +3,19 @@
from . import ModelChecker from . import ModelChecker
## Defines additional metadata for the plug-in. from UM.i18n import i18nCatalog
# i18n_catalog = i18nCatalog("uranium")
# 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():
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
}
}
## 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): def register(app):
return {"extension": ModelChecker.ModelChecker()} return { "tool": ModelChecker.ModelChecker() }

View file

@ -1,8 +1,8 @@
{ {
"name": "Model Checker", "name": "Model Checker",
"author": "Ultimaker", "author": "Ultimaker B.V.",
"version": "0.1", "version": "0.1",
"api": 4, "api": 4,
"description": "Checks models for possible printing issues and give suggestions.", "description": "Checks models and print configuration for possible printing issues and give suggestions.",
"catalog": "cura" "i18n-catalog": "cura"
} }