Merge branch 'fixes_toolbox' of github.com:Ultimaker/Cura into fixes_toolbox

This commit is contained in:
Diego Prado Gesto 2019-07-23 16:19:21 +02:00
commit 8411d43ecd
12 changed files with 74 additions and 56 deletions

View File

@ -9,7 +9,7 @@ DEFAULT_CURA_DISPLAY_NAME = "Ultimaker Cura"
DEFAULT_CURA_VERSION = "master" DEFAULT_CURA_VERSION = "master"
DEFAULT_CURA_BUILD_TYPE = "" DEFAULT_CURA_BUILD_TYPE = ""
DEFAULT_CURA_DEBUG_MODE = False DEFAULT_CURA_DEBUG_MODE = False
DEFAULT_CURA_SDK_VERSION = "6.1.0" DEFAULT_CURA_SDK_VERSION = "6.2.0"
try: try:
from cura.CuraVersion import CuraAppName # type: ignore from cura.CuraVersion import CuraAppName # type: ignore
@ -45,4 +45,4 @@ except ImportError:
# Each release has a fixed SDK version coupled with it. It doesn't make sense to make it configurable because, for # Each release has a fixed SDK version coupled with it. It doesn't make sense to make it configurable because, for
# example Cura 3.2 with SDK version 6.1 will not work. So the SDK version is hard-coded here and left out of the # example Cura 3.2 with SDK version 6.1 will not work. So the SDK version is hard-coded here and left out of the
# CuraVersion.py.in template. # CuraVersion.py.in template.
CuraSDKVersion = "6.1.0" CuraSDKVersion = "6.2.0"

View File

@ -1262,7 +1262,7 @@ class CuraApplication(QtApplication):
@pyqtSlot() @pyqtSlot()
def arrangeObjectsToAllBuildPlates(self) -> None: def arrangeObjectsToAllBuildPlates(self) -> None:
nodes_to_arrange = [] nodes_to_arrange = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()): # type: ignore for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if not isinstance(node, SceneNode): if not isinstance(node, SceneNode):
continue continue
@ -1289,7 +1289,7 @@ class CuraApplication(QtApplication):
def arrangeAll(self) -> None: def arrangeAll(self) -> None:
nodes_to_arrange = [] nodes_to_arrange = []
active_build_plate = self.getMultiBuildPlateModel().activeBuildPlate active_build_plate = self.getMultiBuildPlateModel().activeBuildPlate
for node in DepthFirstIterator(self.getController().getScene().getRoot()): # type: ignore for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if not isinstance(node, SceneNode): if not isinstance(node, SceneNode):
continue continue
@ -1327,7 +1327,7 @@ class CuraApplication(QtApplication):
Logger.log("i", "Reloading all loaded mesh data.") Logger.log("i", "Reloading all loaded mesh data.")
nodes = [] nodes = []
has_merged_nodes = False has_merged_nodes = False
for node in DepthFirstIterator(self.getController().getScene().getRoot()): # type: ignore 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": if node.getName() == "MergedMesh":
has_merged_nodes = True has_merged_nodes = True
@ -1339,9 +1339,9 @@ class CuraApplication(QtApplication):
return return
for node in nodes: for node in nodes:
file_name = node.getMeshData().getFileName() mesh_data = node.getMeshData()
if file_name: if mesh_data and mesh_data.getFileName():
job = ReadMeshJob(file_name) job = ReadMeshJob(mesh_data.getFileName())
job._node = node # type: ignore job._node = node # type: ignore
job.finished.connect(self._reloadMeshFinished) job.finished.connect(self._reloadMeshFinished)
if has_merged_nodes: if has_merged_nodes:

View File

@ -3,6 +3,8 @@
from typing import Optional, TYPE_CHECKING from typing import Optional, TYPE_CHECKING
from numpy import cast
from UM.Application import Application from UM.Application import Application
from UM.Resources import Resources from UM.Resources import Resources
@ -12,6 +14,7 @@ from UM.View.RenderBatch import RenderBatch
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from cura.Scene.CuraSceneNode import CuraSceneNode
if TYPE_CHECKING: if TYPE_CHECKING:
from UM.View.GL.ShaderProgram import ShaderProgram from UM.View.GL.ShaderProgram import ShaderProgram
@ -44,9 +47,9 @@ class PreviewPass(RenderPass):
self._renderer = Application.getInstance().getRenderer() self._renderer = Application.getInstance().getRenderer()
self._shader = None #type: Optional[ShaderProgram] self._shader = None # type: Optional[ShaderProgram]
self._non_printing_shader = None #type: Optional[ShaderProgram] self._non_printing_shader = None # type: Optional[ShaderProgram]
self._support_mesh_shader = None #type: Optional[ShaderProgram] self._support_mesh_shader = None # type: Optional[ShaderProgram]
self._scene = Application.getInstance().getController().getScene() self._scene = Application.getInstance().getController().getScene()
# Set the camera to be used by this render pass # Set the camera to be used by this render pass
@ -83,8 +86,8 @@ class PreviewPass(RenderPass):
batch_support_mesh = RenderBatch(self._support_mesh_shader) batch_support_mesh = RenderBatch(self._support_mesh_shader)
# Fill up the batch with objects that can be sliced. # Fill up the batch with objects that can be sliced.
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. for node in DepthFirstIterator(self._scene.getRoot()):
if hasattr(node, "_outside_buildarea") and not node._outside_buildarea: if hasattr(node, "_outside_buildarea") and not getattr(node, "_outside_buildarea"):
if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible(): if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible():
per_mesh_stack = node.callDecoration("getStack") per_mesh_stack = node.callDecoration("getStack")
if node.callDecoration("isNonThumbnailVisibleMesh"): if node.callDecoration("isNonThumbnailVisibleMesh"):
@ -94,7 +97,7 @@ class PreviewPass(RenderPass):
# Support mesh # Support mesh
uniforms = {} uniforms = {}
shade_factor = 0.6 shade_factor = 0.6
diffuse_color = node.getDiffuseColor() diffuse_color = cast(CuraSceneNode, node).getDiffuseColor()
diffuse_color2 = [ diffuse_color2 = [
diffuse_color[0] * shade_factor, diffuse_color[0] * shade_factor,
diffuse_color[1] * shade_factor, diffuse_color[1] * shade_factor,
@ -106,7 +109,7 @@ class PreviewPass(RenderPass):
else: else:
# Normal scene node # Normal scene node
uniforms = {} uniforms = {}
uniforms["diffuse_color"] = prettier_color(node.getDiffuseColor()) uniforms["diffuse_color"] = prettier_color(cast(CuraSceneNode, node).getDiffuseColor())
batch.addItem(node.getWorldTransformation(), node.getMeshData(), uniforms = uniforms) batch.addItem(node.getWorldTransformation(), node.getMeshData(), uniforms = uniforms)
self.bind() self.bind()

View File

@ -115,7 +115,7 @@ class ExtruderManager(QObject):
selected_nodes = [] # type: List["SceneNode"] selected_nodes = [] # type: List["SceneNode"]
for node in Selection.getAllSelectedObjects(): for node in Selection.getAllSelectedObjects():
if node.callDecoration("isGroup"): if node.callDecoration("isGroup"):
for grouped_node in BreadthFirstIterator(node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. for grouped_node in BreadthFirstIterator(node):
if grouped_node.callDecoration("isGroup"): if grouped_node.callDecoration("isGroup"):
continue continue

View File

@ -949,7 +949,7 @@ class MachineManager(QObject):
# Check to see if any objects are set to print with an extruder that will no longer exist # Check to see if any objects are set to print with an extruder that will no longer exist
root_node = self._application.getController().getScene().getRoot() root_node = self._application.getController().getScene().getRoot()
for node in DepthFirstIterator(root_node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. for node in DepthFirstIterator(root_node):
if node.getMeshData(): if node.getMeshData():
extruder_nr = node.callDecoration("getActiveExtruderPosition") extruder_nr = node.callDecoration("getActiveExtruderPosition")

View File

@ -369,7 +369,7 @@ class CuraEngineBackend(QObject, Backend):
elif job.getResult() == StartJobResult.ObjectSettingError: elif job.getResult() == StartJobResult.ObjectSettingError:
errors = {} errors = {}
for node in DepthFirstIterator(self._application.getController().getScene().getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. for node in DepthFirstIterator(self._application.getController().getScene().getRoot()):
stack = node.callDecoration("getStack") stack = node.callDecoration("getStack")
if not stack: if not stack:
continue continue
@ -438,7 +438,7 @@ class CuraEngineBackend(QObject, Backend):
if not self._application.getPreferences().getValue("general/auto_slice"): if not self._application.getPreferences().getValue("general/auto_slice"):
enable_timer = False enable_timer = False
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. for node in DepthFirstIterator(self._scene.getRoot()):
if node.callDecoration("isBlockSlicing"): if node.callDecoration("isBlockSlicing"):
enable_timer = False enable_timer = False
self.setState(BackendState.Disabled) self.setState(BackendState.Disabled)
@ -460,7 +460,7 @@ class CuraEngineBackend(QObject, Backend):
## Return a dict with number of objects per build plate ## Return a dict with number of objects per build plate
def _numObjectsPerBuildPlate(self) -> Dict[int, int]: def _numObjectsPerBuildPlate(self) -> Dict[int, int]:
num_objects = defaultdict(int) #type: Dict[int, int] num_objects = defaultdict(int) #type: Dict[int, int]
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. for node in DepthFirstIterator(self._scene.getRoot()):
# Only count sliceable objects # Only count sliceable objects
if node.callDecoration("isSliceable"): if node.callDecoration("isSliceable"):
build_plate_number = node.callDecoration("getBuildPlateNumber") build_plate_number = node.callDecoration("getBuildPlateNumber")
@ -548,10 +548,11 @@ class CuraEngineBackend(QObject, Backend):
# Clear out any old gcode # Clear out any old gcode
self._scene.gcode_dict = {} # type: ignore self._scene.gcode_dict = {} # type: ignore
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. for node in DepthFirstIterator(self._scene.getRoot()):
if node.callDecoration("getLayerData"): if node.callDecoration("getLayerData"):
if not build_plate_numbers or node.callDecoration("getBuildPlateNumber") in build_plate_numbers: if not build_plate_numbers or node.callDecoration("getBuildPlateNumber") in build_plate_numbers:
node.getParent().removeChild(node) # We can asume that all nodes have a parent as we're looping through the scene (and filter out root)
cast(SceneNode, node.getParent()).removeChild(node)
def markSliceAll(self) -> None: def markSliceAll(self) -> None:
for build_plate_number in range(self._application.getMultiBuildPlateModel().maxBuildPlate + 1): for build_plate_number in range(self._application.getMultiBuildPlateModel().maxBuildPlate + 1):

View File

@ -11,6 +11,7 @@ import Arcus #For typing.
from UM.Job import Job from UM.Job import Job
from UM.Logger import Logger from UM.Logger import Logger
from UM.Scene.SceneNode import SceneNode
from UM.Settings.ContainerStack import ContainerStack #For typing. from UM.Settings.ContainerStack import ContainerStack #For typing.
from UM.Settings.SettingRelation import SettingRelation #For typing. from UM.Settings.SettingRelation import SettingRelation #For typing.
@ -150,7 +151,7 @@ class StartSliceJob(Job):
# Don't slice if there is a per object setting with an error value. # Don't slice if there is a per object setting with an error value.
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. for node in DepthFirstIterator(self._scene.getRoot()):
if not isinstance(node, CuraSceneNode) or not node.isSelectable(): if not isinstance(node, CuraSceneNode) or not node.isSelectable():
continue continue
@ -160,15 +161,16 @@ class StartSliceJob(Job):
with self._scene.getSceneLock(): with self._scene.getSceneLock():
# Remove old layer data. # Remove old layer data.
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. for node in DepthFirstIterator(self._scene.getRoot()):
if node.callDecoration("getLayerData") and node.callDecoration("getBuildPlateNumber") == self._build_plate_number: if node.callDecoration("getLayerData") and node.callDecoration("getBuildPlateNumber") == self._build_plate_number:
node.getParent().removeChild(node) # Singe we walk through all nodes in the scene, they always have a parent.
cast(SceneNode, node.getParent()).removeChild(node)
break break
# Get the objects in their groups to print. # Get the objects in their groups to print.
object_groups = [] object_groups = []
if stack.getProperty("print_sequence", "value") == "one_at_a_time": if stack.getProperty("print_sequence", "value") == "one_at_a_time":
for node in OneAtATimeIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. for node in OneAtATimeIterator(self._scene.getRoot()):
temp_list = [] temp_list = []
# Node can't be printed, so don't bother sending it. # Node can't be printed, so don't bother sending it.
@ -183,7 +185,8 @@ class StartSliceJob(Job):
children = node.getAllChildren() children = node.getAllChildren()
children.append(node) children.append(node)
for child_node in children: for child_node in children:
if child_node.getMeshData() and child_node.getMeshData().getVertices() is not None: mesh_data = child_node.getMeshData()
if mesh_data and mesh_data.getVertices() is not None:
temp_list.append(child_node) temp_list.append(child_node)
if temp_list: if temp_list:
@ -194,8 +197,9 @@ class StartSliceJob(Job):
else: else:
temp_list = [] temp_list = []
has_printing_mesh = False has_printing_mesh = False
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. for node in DepthFirstIterator(self._scene.getRoot()):
if node.callDecoration("isSliceable") and node.getMeshData() and node.getMeshData().getVertices() is not None: mesh_data = node.getMeshData()
if node.callDecoration("isSliceable") and mesh_data and mesh_data.getVertices() is not None:
is_non_printing_mesh = bool(node.callDecoration("isNonPrintingMesh")) is_non_printing_mesh = bool(node.callDecoration("isNonPrintingMesh"))
# Find a reason not to add the node # Find a reason not to add the node
@ -210,7 +214,7 @@ class StartSliceJob(Job):
Job.yieldThread() Job.yieldThread()
#If the list doesn't have any model with suitable settings then clean the list # If the list doesn't have any model with suitable settings then clean the list
# otherwise CuraEngine will crash # otherwise CuraEngine will crash
if not has_printing_mesh: if not has_printing_mesh:
temp_list.clear() temp_list.clear()
@ -261,10 +265,14 @@ class StartSliceJob(Job):
for group in filtered_object_groups: for group in filtered_object_groups:
group_message = self._slice_message.addRepeatedMessage("object_lists") group_message = self._slice_message.addRepeatedMessage("object_lists")
if group[0].getParent() is not None and group[0].getParent().callDecoration("isGroup"): parent = group[0].getParent()
self._handlePerObjectSettings(group[0].getParent(), group_message) if parent is not None and parent.callDecoration("isGroup"):
self._handlePerObjectSettings(cast(CuraSceneNode, parent), group_message)
for object in group: for object in group:
mesh_data = object.getMeshData() mesh_data = object.getMeshData()
if mesh_data is None:
continue
rot_scale = object.getWorldTransformation().getTransposed().getData()[0:3, 0:3] rot_scale = object.getWorldTransformation().getTransposed().getData()[0:3, 0:3]
translate = object.getWorldTransformation().getData()[:3, 3] translate = object.getWorldTransformation().getData()[:3, 3]
@ -288,7 +296,7 @@ class StartSliceJob(Job):
obj.vertices = flat_verts obj.vertices = flat_verts
self._handlePerObjectSettings(object, obj) self._handlePerObjectSettings(cast(CuraSceneNode, object), obj)
Job.yieldThread() Job.yieldThread()

View File

@ -218,10 +218,10 @@ class SimulationView(CuraView):
if theme is not None: if theme is not None:
self._ghost_shader.setUniformValue("u_color", Color(*theme.getColor("layerview_ghost").getRgb())) self._ghost_shader.setUniformValue("u_color", Color(*theme.getColor("layerview_ghost").getRgb()))
for node in DepthFirstIterator(scene.getRoot()): # type: ignore for node in DepthFirstIterator(scene.getRoot()):
# We do not want to render ConvexHullNode as it conflicts with the bottom layers. # We do not want to render ConvexHullNode as it conflicts with the bottom layers.
# However, it is somewhat relevant when the node is selected, so do render it then. # However, it is somewhat relevant when the node is selected, so do render it then.
if type(node) is ConvexHullNode and not Selection.isSelected(node.getWatchedNode()): if type(node) is ConvexHullNode and not Selection.isSelected(cast(ConvexHullNode, node).getWatchedNode()):
continue continue
if not node.render(renderer): if not node.render(renderer):

View File

@ -243,10 +243,11 @@ Item
enabled: !contextMenuButton.enabled enabled: !contextMenuButton.enabled
} }
MonitorInfoBlurb // TODO: uncomment this tooltip as soon as the required firmware is released
{ // MonitorInfoBlurb
id: contextMenuDisabledInfo // {
text: catalog.i18nc("@info", "Please update your printer's firmware to manage the queue remotely.") // id: contextMenuDisabledInfo
target: contextMenuButton // text: catalog.i18nc("@info", "Please update your printer's firmware to manage the queue remotely.")
} // target: contextMenuButton
// }
} }

View File

@ -81,7 +81,7 @@ Item
mipmap: true mipmap: true
} }
} }
Item Item
{ {
@ -99,7 +99,7 @@ Item
height: 18 * screenScaleFactor // TODO: Theme! height: 18 * screenScaleFactor // TODO: Theme!
width: parent.width width: parent.width
radius: 2 * screenScaleFactor // TODO: Theme! radius: 2 * screenScaleFactor // TODO: Theme!
Label Label
{ {
text: printer && printer.name ? printer.name : "" text: printer && printer.name ? printer.name : ""
@ -202,12 +202,13 @@ Item
enabled: !contextMenuButton.enabled enabled: !contextMenuButton.enabled
} }
MonitorInfoBlurb // TODO: uncomment this tooltip as soon as the required firmware is released
{ // MonitorInfoBlurb
id: contextMenuDisabledInfo // {
text: catalog.i18nc("@info", "Please update your printer's firmware to manage the queue remotely.") // id: contextMenuDisabledInfo
target: contextMenuButton // text: catalog.i18nc("@info", "Please update your printer's firmware to manage the queue remotely.")
} // target: contextMenuButton
// }
CameraButton CameraButton
{ {
@ -454,4 +455,4 @@ Item
id: overrideConfirmationDialog id: overrideConfirmationDialog
printer: base.printer printer: base.printer
} }
} }

View File

@ -174,9 +174,13 @@ class CloudApiClient:
model: Type[CloudApiClientModel], model: Type[CloudApiClientModel],
) -> None: ) -> None:
def parse() -> None: def parse() -> None:
# Don't try to parse the reply if we didn't get one
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) is None:
return
status_code, response = self._parseReply(reply) status_code, response = self._parseReply(reply)
self._anti_gc_callbacks.remove(parse) self._anti_gc_callbacks.remove(parse)
return self._parseModels(response, on_finished, model) self._parseModels(response, on_finished, model)
return
self._anti_gc_callbacks.append(parse) self._anti_gc_callbacks.append(parse)
reply.finished.connect(parse) reply.finished.connect(parse)

View File

@ -6,7 +6,7 @@ import pytest
from unittest.mock import patch from unittest.mock import patch
class TestConvexHullDecorator(SceneNodeDecorator): class MockedConvexHullDecorator(SceneNodeDecorator):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
@ -14,7 +14,7 @@ class TestConvexHullDecorator(SceneNodeDecorator):
return Polygon([[5, 5], [-5, 5], [-5, -5], [5, -5]]) return Polygon([[5, 5], [-5, 5], [-5, -5], [5, -5]])
class TestInvalidConvexHullDecorator(SceneNodeDecorator): class InvalidConvexHullDecorator(SceneNodeDecorator):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
@ -34,16 +34,16 @@ class TestCollidesWithAreas:
assert not cura_scene_node.collidesWithAreas([Polygon([[10, 10], [-10, 10], [-10, -10], [10, -10]])]) assert not cura_scene_node.collidesWithAreas([Polygon([[10, 10], [-10, 10], [-10, -10], [10, -10]])])
def test_convexHullIntersects(self, cura_scene_node): def test_convexHullIntersects(self, cura_scene_node):
cura_scene_node.addDecorator(TestConvexHullDecorator()) cura_scene_node.addDecorator(MockedConvexHullDecorator())
assert cura_scene_node.collidesWithAreas([Polygon([[10, 10], [-10, 10], [-10, -10], [10, -10]])]) assert cura_scene_node.collidesWithAreas([Polygon([[10, 10], [-10, 10], [-10, -10], [10, -10]])])
def test_convexHullNoIntersection(self, cura_scene_node): def test_convexHullNoIntersection(self, cura_scene_node):
cura_scene_node.addDecorator(TestConvexHullDecorator()) cura_scene_node.addDecorator(MockedConvexHullDecorator())
assert not cura_scene_node.collidesWithAreas([Polygon([[60, 60], [40, 60], [40, 40], [60, 40]])]) assert not cura_scene_node.collidesWithAreas([Polygon([[60, 60], [40, 60], [40, 40], [60, 40]])])
def test_invalidConvexHull(self, cura_scene_node): def test_invalidConvexHull(self, cura_scene_node):
cura_scene_node.addDecorator(TestInvalidConvexHullDecorator()) cura_scene_node.addDecorator(InvalidConvexHullDecorator())
assert not cura_scene_node.collidesWithAreas([Polygon([[10, 10], [-10, 10], [-10, -10], [10, -10]])]) assert not cura_scene_node.collidesWithAreas([Polygon([[10, 10], [-10, 10], [-10, -10], [10, -10]])])